]> git.sesse.net Git - ultimatescore/commitdiff
Initial checkin (sans Nageru theme).
authorSteinar H. Gunderson <sgunderson@bigfoot.com>
Sat, 22 Apr 2017 08:35:28 +0000 (10:35 +0200)
committerSteinar H. Gunderson <sgunderson@bigfoot.com>
Sat, 22 Apr 2017 08:35:28 +0000 (10:35 +0200)
16 files changed:
README [new file with mode: 0644]
bg.svg [new file with mode: 0644]
bg2.svg [new file with mode: 0644]
casparcg.config [new file with mode: 0644]
client/Makefile [new file with mode: 0644]
client/acmp_client.cpp [new file with mode: 0644]
client/acmp_client.h [new file with mode: 0644]
client/main.cpp [new file with mode: 0644]
client/mainwindow.cpp [new file with mode: 0644]
client/mainwindow.h [new file with mode: 0644]
client/mainwindow.ui [new file with mode: 0644]
client/post_to_main_thread.h [new file with mode: 0644]
client/ui_mainwindow.h [new file with mode: 0644]
lowerthird-bg.png [new file with mode: 0644]
lowerthird-bg2.png [new file with mode: 0644]
score.html [new file with mode: 0644]

diff --git a/README b/README
new file mode 100644 (file)
index 0000000..35a8e6f
--- /dev/null
+++ b/README
@@ -0,0 +1,16 @@
+How to prepare:
+
+  Copy all .html and .png files into your CasparCG templates/ directory.
+  Copy casparcg.config into your CasparCG directory. cd into client/
+  and run make to compile the client.
+
+How to run:
+
+  1. rm /tmp/caspar.sock.
+  2. Run CasparCG. It will hang until Nageru connects.
+  3. Start Nageru with -c 3 -t ultimate.lua.
+  4. Start the client.
+  5. Switch to channel 5 and turn on the overlay when it's started.
+
+If you need to restart CasparCG for any reason, you'll need to redo steps 1 and 2
+in order. Nageru and the client can stay up; they will reconnect automatically.
diff --git a/bg.svg b/bg.svg
new file mode 100644 (file)
index 0000000..2897c28
--- /dev/null
+++ b/bg.svg
@@ -0,0 +1,312 @@
+<?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="210mm"
+   height="297mm"
+   viewBox="0 0 210 297"
+   version="1.1"
+   id="svg11445"
+   inkscape:version="0.92.1 r15371"
+   sodipodi:docname="bg.svg">
+  <defs
+     id="defs11439">
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient12837">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0"
+         id="stop12833" />
+      <stop
+         style="stop-color:#f3f3f3;stop-opacity:0.95686275"
+         offset="1"
+         id="stop12835" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient758"
+       spreadMethod="pad"
+       gradientTransform="matrix(1041.9775,0,0,-1041.9775,1161.687,5231.604)"
+       gradientUnits="userSpaceOnUse"
+       y2="0"
+       x2="1"
+       y1="0"
+       x1="0">
+      <stop
+         id="stop12314"
+         offset="0"
+         style="stop-opacity:1;stop-color:#ffffff" />
+      <stop
+         id="stop12316"
+         offset="0.10691959"
+         style="stop-opacity:1;stop-color:#ffffff" />
+      <stop
+         id="stop12318"
+         offset="1"
+         style="stop-opacity:1;stop-color:#231f20" />
+    </linearGradient>
+    <clipPath
+       clipPathUnits="userSpaceOnUse"
+       id="clipPath750">
+      <path
+         d="m 1405.364,5194.503 c -50.371,-43.444 -89.981,-61.977 -127.217,-61.319 -26.092,0.466 -51.105,10.931 -75.785,31.831 8.139,-14.917 19.583,-32.813 34.809,-50.074 20.852,-23.635 44.648,-41.487 70.716,-53.059 32.528,-14.439 68.85,-19.132 107.953,-13.948 89.028,11.79 239.813,79.55 283.148,163.123 l 4.079,7.872 6.725,-5.78 c 71.402,-61.345 236.579,-67.907 323.568,-45.6 37.707,9.668 69.318,27.248 93.953,52.243 42.102,42.724 55.918,99.005 60.318,135.999 -15.08,-28.557 -34.359,-47.574 -58.362,-57.734 -50.856,-21.528 -122.323,-5.857 -246.676,54.096 -86.553,41.731 -166.863,32.344 -195.296,-22.832 l -4.082,-7.921 -6.746,5.825 c -46.979,40.567 -124.982,19.292 -189.696,-51.744 -30.269,-33.219 -57.095,-60.005 -81.409,-80.978 m -243.677,35.49 23.29,-27.449 c 31.108,-36.662 61.664,-54.761 93.414,-55.321 50.042,-0.886 109.26,40.302 198.007,137.713 67.27,73.852 149.202,96.884 202.613,58.168 5.161,8.218 11.395,15.426 18.56,21.608 41.537,35.828 114.363,37.093 191.121,0.085 118.703,-57.229 189.016,-73.322 235.105,-53.813 29.241,12.378 50.832,40.572 65.995,86.197 l 11.352,34.163 2.314,-35.926 c 0.069,-1.138 1.71,-28.327 -5.536,-64.815 -6.707,-33.796 -22.67,-82.162 -60.607,-120.664 -26.454,-26.843 -60.256,-45.68 -100.471,-55.988 -47.787,-12.251 -112.849,-15.536 -174.047,-8.786 -66.179,7.296 -120.926,25.65 -155.507,51.966 -22.25,-37.322 -66.189,-74.795 -124.86,-106.275 -54.248,-29.115 -115.838,-50.362 -164.744,-56.837 -94.828,-12.565 -156.408,32.223 -191.371,72.011 -37.596,42.783 -52.732,87.876 -53.357,89.776 z"
+         id="path748"
+         inkscape:connector-curvature="0" />
+    </clipPath>
+    <linearGradient
+       x1="0"
+       y1="0"
+       x2="1"
+       y2="0"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1041.9775,0,0,-1041.9775,1161.687,5231.604)"
+       spreadMethod="pad"
+       id="whatevs-1">
+      <stop
+         style="stop-opacity:1;stop-color:#cccccc"
+         offset="0"
+         id="stop752" />
+      <stop
+         style="stop-opacity:1;stop-color:#ffffff"
+         offset="1"
+         id="stop756" />
+    </linearGradient>
+    <clipPath
+       clipPathUnits="userSpaceOnUse"
+       id="clipPath860">
+      <path
+         d="m 3217.535,2532.398 c -55.906,37.49 -84.611,71.163 -93.965,107.673 -6.549,25.579 -3.046,52.799 10.729,82.485 -12.371,-11.945 -26.763,-27.909 -39.52,-47.399 -17.471,-26.688 -28.504,-54.691 -32.802,-83.237 -5.36,-35.609 -0.191,-72.302 15.359,-109.072 35.383,-83.707 141.955,-212.642 235.125,-232.507 l 8.774,-1.868 -3.831,-8.107 c -40.703,-86.133 -2.786,-249.053 42.315,-327.944 19.55,-34.202 45.182,-60.327 76.181,-77.656 52.982,-29.619 111.602,-28 148.877,-22.365 -31.907,7.053 -55.635,20.762 -71.988,41.45 -34.647,43.85 -38.533,117.783 -13.401,255.199 17.496,95.648 -13.207,171.491 -74.673,184.432 l -8.82,1.86 3.871,8.139 c 26.98,56.725 -14.709,127.125 -101.381,171.208 -40.533,20.621 -73.864,39.614 -100.85,57.709 m -30.752,247.276 -20.535,-30.088 c -27.429,-40.19 -36.884,-74.856 -28.914,-105.984 12.565,-49.067 68.637,-95.794 187.491,-156.253 90.105,-45.826 134.56,-119.584 111.116,-182.084 9.405,-2.829 18.11,-6.984 26.063,-12.314 46.1,-30.909 66.872,-101.636 51.359,-186.457 -23.988,-131.175 -20.83,-204.099 10.57,-243.836 19.92,-25.204 53.224,-38.706 101.809,-41.26 l 36.38,-1.911 -34.434,-11.894 c -1.091,-0.379 -27.18,-9.273 -64.726,-11.99 -34.773,-2.531 -86.248,0.073 -133.992,26.764 -33.289,18.601 -60.736,46.533 -81.582,83.003 -24.781,43.339 -45.44,105.943 -55.277,167.464 -10.629,66.533 -7.41,124.876 8.992,165.672 -42.39,11.701 -90.738,44.514 -137.196,93.316 -42.964,45.119 -80.217,99.509 -99.656,145.493 -37.7,89.151 -10.517,161.257 18.918,206.044 31.658,48.161 71.595,75.026 73.28,76.151 z"
+         id="path858"
+         inkscape:connector-curvature="0" />
+    </clipPath>
+    <linearGradient
+       x1="0"
+       y1="0"
+       x2="1"
+       y2="0"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(616.29688,0,0,-616.29688,3045.8252,2287.2598)"
+       spreadMethod="pad"
+       id="linearGradient868">
+      <stop
+         style="stop-opacity:1;stop-color:#ffffff"
+         offset="0"
+         id="stop862" />
+      <stop
+         style="stop-opacity:1;stop-color:#ffffff"
+         offset="0.10691959"
+         id="stop864" />
+      <stop
+         style="stop-opacity:1;stop-color:#231f20"
+         offset="1"
+         id="stop866" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient758"
+       id="linearGradient12339-5"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(38.597503,0,0,38.597503,12.794587,52.903821)"
+       x1="0"
+       y1="0"
+       x2="1"
+       y2="0"
+       spreadMethod="pad" />
+    <clipPath
+       clipPathUnits="userSpaceOnUse"
+       id="clipPath836">
+      <path
+         d="m 3287.719,2298.145 c -59.434,-102.862 -115.35,-157.371 -178.537,-178.134 -44.267,-14.548 -92.606,-11.588 -146.502,9.167 22.492,-20.371 52.302,-43.835 88.116,-64.003 49.051,-27.612 99.663,-43.747 150.431,-47.953 63.332,-5.243 127.341,8.172 190.252,39.886 143.218,72.167 357.711,275.011 381.711,441.402 l 2.26,15.669 14.73,-5.792 c 156.455,-61.525 438.868,24.431 572.474,113.136 57.917,38.451 100.897,86.663 127.765,143.287 45.914,96.784 36.16,199.816 21.852,264.786 -8.662,-57.009 -30.003,-100.412 -64.513,-131.645 -73.131,-66.17 -202.866,-81.714 -447.792,-53.649 -170.478,19.541 -300.407,-43.45 -315.954,-153.208 l -2.232,-15.757 -14.795,5.861 c -103.059,40.827 -222.109,-40.871 -289.527,-198.68 -31.534,-73.801 -61.053,-134.725 -89.739,-184.373 m -431.794,-83.271 55.395,-32.62 c 73.995,-43.561 136.159,-56.13 190.032,-38.429 84.915,27.904 160.591,132.138 253.05,348.544 70.081,164.064 194.728,251.027 307.542,217.106 3.876,16.887 10.158,32.706 18.611,47.332 49.005,84.824 171.089,129.73 322.27,112.406 233.803,-26.795 361.834,-12.641 428.108,47.326 42.046,38.045 61.898,98.28 60.668,184.132 l -0.921,64.283 25.005,-59.233 c 0.788,-1.879 19.521,-46.767 28.734,-112.562 8.542,-60.93 10.025,-151.872 -31.356,-239.096 -28.832,-60.802 -74.777,-112.418 -136.544,-153.422 -73.391,-48.735 -181.194,-92.495 -288.363,-117.053 -115.904,-26.552 -219.014,-27.759 -292.783,-3.682 -15.61,-76.019 -67.7,-165.02 -148.157,-252.569 -74.39,-80.964 -165.777,-152.97 -244.458,-192.617 -152.537,-76.88 -282.702,-37.516 -365.037,9.053 -88.526,50.066 -140.539,117.226 -142.714,120.061 z"
+         id="path834"
+         inkscape:connector-curvature="0" />
+    </clipPath>
+    <linearGradient
+       x1="0"
+       y1="0"
+       x2="1"
+       y2="0"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(56.469845,0,0,56.469845,28.865443,46.818235)"
+       spreadMethod="pad"
+       id="linearGradient844">
+      <stop
+         style="stop-opacity:1;stop-color:#ffffff"
+         offset="0"
+         id="stop838" />
+      <stop
+         style="stop-opacity:1;stop-color:#ffffff"
+         offset="0.10691959"
+         id="stop840" />
+      <stop
+         style="stop-opacity:1;stop-color:#231f20"
+         offset="1"
+         id="stop842" />
+    </linearGradient>
+    <clipPath
+       clipPathUnits="userSpaceOnUse"
+       id="clipPath1992">
+      <path
+         d="m 3311.591,5872.595 c -82.332,17.698 -131.288,43.547 -159.056,81.656 -19.451,26.7 -27.979,59.953 -25.715,100.834 -8.851,-19.611 -18.186,-44.832 -23.99,-73.387 -7.947,-39.1 -7.809,-76.762 0.406,-111.922 10.258,-43.87 33.247,-84.133 68.338,-119.667 79.877,-80.906 263.431,-181.44 380.912,-161.392 l 11.063,1.89 -0.701,-11.206 c -7.436,-118.943 112.048,-290.754 200.99,-361.568 38.553,-30.704 80.441,-49.203 124.49,-54.996 75.289,-9.895 142.674,19.121 183.391,42.925 -40.35,-6.577 -74.274,-1.626 -102.86,14.855 -60.566,34.923 -99.307,119.057 -133.706,290.409 -23.942,119.266 -94.735,193.201 -172.166,179.791 l -11.115,-1.925 0.732,11.256 c 5.101,78.417 -75.94,140.943 -197.081,152.06 -56.659,5.205 -104.189,11.846 -143.932,20.387 m -150.209,273.174 -9.936,-44.477 c -13.274,-59.407 -8.221,-104.083 15.453,-136.572 37.312,-51.21 124.118,-79.564 290.246,-94.813 125.942,-11.551 211.755,-76.704 213.439,-160.199 12.238,1.06 24.277,0.268 35.99,-2.249 67.891,-14.588 124.771,-87.169 146.004,-192.938 32.84,-163.568 70.271,-246.86 125.156,-278.51 34.829,-20.079 79.784,-20.356 137.433,-0.831 l 43.169,14.619 -34.514,-29.765 c -1.097,-0.94 -27.299,-23.36 -69.677,-43.899 -39.251,-19.03 -100.281,-39.834 -168.131,-30.92 -47.303,6.223 -92.131,25.974 -133.249,58.713 -48.855,38.906 -101.847,102.103 -141.753,169.055 -43.16,72.403 -66.426,141.703 -66.252,196.714 -54.678,-6.022 -126.065,9.736 -202.651,44.952 -70.82,32.556 -139.297,78.527 -183.178,122.975 -85.089,86.166 -86.874,182.55 -73.389,248.234 14.5,70.629 48.48,120.346 49.921,122.426 z"
+         id="path1990"
+         inkscape:connector-curvature="0" />
+    </clipPath>
+    <linearGradient
+       x1="0"
+       y1="0"
+       x2="1"
+       y2="0"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1079.04,0,0,-1079.04,3079.2959,5644.4287)"
+       spreadMethod="pad"
+       id="linearGradient2000">
+      <stop
+         style="stop-opacity:1;stop-color:#ffffff"
+         offset="0"
+         id="stop1994" />
+      <stop
+         style="stop-opacity:1;stop-color:#ffffff"
+         offset="0.10691959"
+         id="stop1996" />
+      <stop
+         style="stop-opacity:1;stop-color:#231f20"
+         offset="1"
+         id="stop1998" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient12837"
+       id="linearGradient12839"
+       x1="24.9897"
+       y1="43.494854"
+       x2="112.07853"
+       y2="49.820049"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#whatevs-1"
+       id="linearGradient12893"
+       x1="17.733486"
+       y1="48.622395"
+       x2="23.333635"
+       y2="48.622395"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.26701219,0,0,0.64081556,13.000775,16.966749)" />
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="2.8"
+     inkscape:cx="179.49327"
+     inkscape:cy="935.91734"
+     inkscape:document-units="mm"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     inkscape:window-width="1920"
+     inkscape:window-height="991"
+     inkscape:window-x="0"
+     inkscape:window-y="26"
+     inkscape:window-maximized="1" />
+  <metadata
+     id="metadata11442">
+    <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></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1">
+    <rect
+       style="opacity:1;fill:#00ffff;fill-opacity:1;stroke:#9e9e9e;stroke-width:0.025;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+       id="rect11477"
+       width="146.65475"
+       height="61.988094"
+       x="3.0238097"
+       y="23.723215" />
+    <rect
+       style="opacity:1;fill:url(#linearGradient12839);fill-opacity:1;stroke:#9e9e9e;stroke-width:0.025;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+       id="rect11473"
+       width="105"
+       height="11.8"
+       x="17.764881"
+       y="42.244045"
+       inkscape:export-xdpi="253.46324"
+       inkscape:export-ydpi="253.46324" />
+    <rect
+       style="opacity:1;fill:url(#linearGradient12893);fill-opacity:1;stroke:#f1c6f1;stroke-width:0.01034123;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+       id="rect12885"
+       width="1.4886327"
+       height="11.686779"
+       x="17.73917"
+       y="42.281345" />
+    <path
+       inkscape:connector-curvature="0"
+       id="path810-9"
+       style="opacity:0.117;fill:url(#linearGradient12339-5);stroke:none;stroke-width:0.03704255"
+       d="m 12.794586,52.963496 0.862722,1.016781 c 4.098478,4.603131 8.523732,-0.582855 10.794976,-3.05201 2.491852,-2.735667 5.526822,-3.58883 7.505302,-2.154691 0.191176,-0.304416 0.4221,-0.571418 0.68751,-0.800415 1.538636,-1.327161 4.236297,-1.37402 7.079609,-0.0031 4.397063,2.119907 7.001633,2.716034 8.708888,1.993371 1.083163,-0.458514 1.88295,-1.502891 2.444624,-3.192957 l 0.420506,-1.265484 0.08571,1.33079 c 0.0024,0.04215 0.06334,1.049304 -0.205067,2.400912 -0.248443,1.25189 -0.839753,3.04349 -2.245037,4.469703 -0.979923,0.994333 -2.232035,1.692104 -3.721699,2.073938 -1.770157,0.453809 -4.180218,0.575493 -6.447148,0.325456 -2.45144,-0.270262 -4.479408,-0.950142 -5.760377,-1.924954 -0.824196,1.382503 -2.451808,2.770599 -4.625132,3.936698 -2.009484,1.078493 -4.290935,1.865536 -6.102538,2.105387 -3.51267,0.46544 -5.793751,-1.193623 -7.08887,-2.667471 -1.392651,-1.584792 -1.953327,-3.255151 -1.976478,-3.325532 z"
+       sodipodi:nodetypes="cccccccccccccccccccc" />
+    <g
+       id="g966"
+       transform="matrix(0.02991036,0,0,-0.02991036,-6.3821215,124.57045)"
+       style="opacity:0.32000002">
+      <g
+         id="g964">
+        <path
+           inkscape:connector-curvature="0"
+           id="path962"
+           style="fill:url(#linearGradient868);stroke:none"
+           d="m 3186.783,2779.674 -20.535,-30.088 c -27.429,-40.19 -36.884,-74.856 -28.914,-105.984 12.565,-49.067 68.637,-95.794 187.491,-156.253 90.105,-45.826 134.56,-119.584 111.116,-182.084 9.405,-2.829 18.11,-6.984 26.063,-12.314 46.1,-30.909 66.872,-101.636 51.359,-186.457 -23.988,-131.175 -20.83,-204.099 10.57,-243.836 19.92,-25.204 53.224,-38.706 101.809,-41.26 l 36.38,-1.911 -34.434,-11.894 c -1.091,-0.379 -27.18,-9.273 -64.726,-11.99 -34.773,-2.531 -86.248,0.073 -133.992,26.764 -33.289,18.601 -60.736,46.533 -81.582,83.003 -24.781,43.339 -45.44,105.943 -55.277,167.464 -10.629,66.533 -7.41,124.876 8.992,165.672 -42.39,11.701 -90.738,44.514 -137.196,93.316 -42.964,45.119 -80.217,99.509 -99.656,145.493 -37.7,89.151 -10.517,161.257 18.918,206.044 31.658,48.161 71.595,75.026 73.28,76.151 z"
+           sodipodi:nodetypes="ccccccccccccccccccccc" />
+      </g>
+    </g>
+    <path
+       inkscape:connector-curvature="0"
+       id="path942"
+       style="opacity:0.106;fill:url(#linearGradient844);stroke:none;stroke-width:0.03334784"
+       d="m 28.865456,58.931351 1.847304,1.087807 c 2.467573,1.452665 4.540608,1.871814 6.337156,1.281524 2.831732,-0.930538 5.355363,-4.406517 8.438671,-11.62319 2.33705,-5.47118 6.493758,-8.371208 10.255862,-7.240016 0.129256,-0.563145 0.338747,-1.090674 0.620636,-1.57842 1.634211,-2.828697 5.705449,-4.326215 10.747009,-3.748497 7.796825,0.893555 12.066382,0.42155 14.276477,-1.57822 1.402143,-1.268718 2.064164,-3.277426 2.023146,-6.140404 l -0.03071,-2.1437 0.833863,1.975293 c 0.02628,0.06266 0.650983,1.559578 0.958217,3.7537 0.284857,2.031883 0.334312,5.064603 -1.045655,7.973335 -0.961485,2.027615 -2.493652,3.748897 -4.553448,5.116292 -2.447431,1.625207 -6.042428,3.084508 -9.616283,3.903465 -3.865148,0.885452 -7.303644,0.925702 -9.76368,0.122786 -0.52056,2.53507 -2.257649,5.503061 -4.940716,8.422631 C 52.772559,61.215712 49.725,63.616956 47.101158,64.939098 42.01437,67.50288 37.673648,66.190176 34.927954,64.6372 31.975803,62.967607 30.241282,60.727966 30.16875,60.633425 Z"
+       sodipodi:nodetypes="ccccccccccccccccccccc" />
+    <g
+       id="g2084"
+       transform="matrix(0.0224278,0,0,-0.0224278,32.577361,174.7361)"
+       style="opacity:0.27">
+      <g
+         id="g2082"
+         style="">
+        <path
+           sodipodi:nodetypes="ccccccccccccccccccccc"
+           inkscape:connector-curvature="0"
+           id="path2080"
+           style="fill:url(#linearGradient2000);stroke:none"
+           d="m 3161.382,6145.769 -9.936,-44.477 c -13.274,-59.407 -8.221,-104.083 15.453,-136.572 37.312,-51.21 124.118,-79.564 290.246,-94.813 125.942,-11.551 211.755,-76.704 213.439,-160.199 12.238,1.06 24.277,0.268 35.99,-2.249 67.891,-14.588 124.771,-87.169 146.004,-192.938 32.84,-163.568 70.271,-246.86 125.156,-278.51 34.829,-20.079 79.784,-20.356 137.433,-0.831 l 43.169,14.619 -34.514,-29.765 c -1.097,-0.94 -27.299,-23.36 -69.677,-43.899 -39.251,-19.03 -100.281,-39.834 -168.131,-30.92 -47.303,6.223 -92.131,25.974 -133.249,58.713 -48.855,38.906 -101.847,102.103 -141.753,169.055 -43.16,72.403 -66.426,141.703 -66.252,196.714 -54.678,-6.022 -126.065,9.736 -202.651,44.952 -70.82,32.556 -139.297,78.527 -183.178,122.975 -85.089,86.166 -86.874,182.55 -73.389,248.234 14.5,70.629 48.48,120.346 49.921,122.426 z" />
+      </g>
+    </g>
+    <path
+       style="opacity:1;fill:#00ffff;fill-opacity:1;stroke:#9e9e9e;stroke-width:0.025;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+       d="M 4.1806233,53.981494 V 115.96943 H 150.83575 V 53.981494 Z"
+       id="rect11477-3"
+       inkscape:connector-curvature="0" />
+    <path
+       style="opacity:1;fill:#00ffff;fill-opacity:1;stroke:#9e9e9e;stroke-width:0.025;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+       d="M 5.2496761,-19.743869 V 42.244072 H 151.90481 v -61.987941 z"
+       id="rect11477-3-7"
+       inkscape:connector-curvature="0" />
+  </g>
+</svg>
diff --git a/bg2.svg b/bg2.svg
new file mode 100644 (file)
index 0000000..ff6d119
--- /dev/null
+++ b/bg2.svg
@@ -0,0 +1,325 @@
+<?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="210mm"
+   height="297mm"
+   viewBox="0 0 210 297"
+   version="1.1"
+   id="svg11445"
+   inkscape:version="0.92.1 r15371"
+   sodipodi:docname="bg2.svg">
+  <defs
+     id="defs11439">
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient877">
+      <stop
+         style="stop-color:#4444cc;stop-opacity:1"
+         offset="0"
+         id="stop873" />
+      <stop
+         style="stop-color:#3535c2;stop-opacity:1"
+         offset="1"
+         id="stop875" />
+    </linearGradient>
+    <linearGradient
+       id="blueish-6-7-5"
+       inkscape:collect="always">
+      <stop
+         id="stop858"
+         offset="0"
+         style="stop-color:#3333aa;stop-opacity:1" />
+      <stop
+         id="stop860"
+         offset="1"
+         style="stop-color:#4444cc;stop-opacity:1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient758"
+       spreadMethod="pad"
+       gradientTransform="matrix(1041.9775,0,0,-1041.9775,1161.687,5231.604)"
+       gradientUnits="userSpaceOnUse"
+       y2="0"
+       x2="1"
+       y1="0"
+       x1="0">
+      <stop
+         id="stop12314"
+         offset="0"
+         style="stop-opacity:1;stop-color:#ffffff" />
+      <stop
+         id="stop12316"
+         offset="0.10691959"
+         style="stop-opacity:1;stop-color:#ffffff" />
+      <stop
+         id="stop12318"
+         offset="1"
+         style="stop-opacity:1;stop-color:#231f20" />
+    </linearGradient>
+    <clipPath
+       clipPathUnits="userSpaceOnUse"
+       id="clipPath750">
+      <path
+         d="m 1405.364,5194.503 c -50.371,-43.444 -89.981,-61.977 -127.217,-61.319 -26.092,0.466 -51.105,10.931 -75.785,31.831 8.139,-14.917 19.583,-32.813 34.809,-50.074 20.852,-23.635 44.648,-41.487 70.716,-53.059 32.528,-14.439 68.85,-19.132 107.953,-13.948 89.028,11.79 239.813,79.55 283.148,163.123 l 4.079,7.872 6.725,-5.78 c 71.402,-61.345 236.579,-67.907 323.568,-45.6 37.707,9.668 69.318,27.248 93.953,52.243 42.102,42.724 55.918,99.005 60.318,135.999 -15.08,-28.557 -34.359,-47.574 -58.362,-57.734 -50.856,-21.528 -122.323,-5.857 -246.676,54.096 -86.553,41.731 -166.863,32.344 -195.296,-22.832 l -4.082,-7.921 -6.746,5.825 c -46.979,40.567 -124.982,19.292 -189.696,-51.744 -30.269,-33.219 -57.095,-60.005 -81.409,-80.978 m -243.677,35.49 23.29,-27.449 c 31.108,-36.662 61.664,-54.761 93.414,-55.321 50.042,-0.886 109.26,40.302 198.007,137.713 67.27,73.852 149.202,96.884 202.613,58.168 5.161,8.218 11.395,15.426 18.56,21.608 41.537,35.828 114.363,37.093 191.121,0.085 118.703,-57.229 189.016,-73.322 235.105,-53.813 29.241,12.378 50.832,40.572 65.995,86.197 l 11.352,34.163 2.314,-35.926 c 0.069,-1.138 1.71,-28.327 -5.536,-64.815 -6.707,-33.796 -22.67,-82.162 -60.607,-120.664 -26.454,-26.843 -60.256,-45.68 -100.471,-55.988 -47.787,-12.251 -112.849,-15.536 -174.047,-8.786 -66.179,7.296 -120.926,25.65 -155.507,51.966 -22.25,-37.322 -66.189,-74.795 -124.86,-106.275 -54.248,-29.115 -115.838,-50.362 -164.744,-56.837 -94.828,-12.565 -156.408,32.223 -191.371,72.011 -37.596,42.783 -52.732,87.876 -53.357,89.776 z"
+         id="path748"
+         inkscape:connector-curvature="0" />
+    </clipPath>
+    <linearGradient
+       x1="0"
+       y1="0"
+       x2="1"
+       y2="0"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1041.9775,0,0,-1041.9775,1161.687,5231.604)"
+       spreadMethod="pad"
+       id="whatevs-1">
+      <stop
+         style="stop-opacity:1;stop-color:#cccccc"
+         offset="0"
+         id="stop752" />
+      <stop
+         style="stop-opacity:1;stop-color:#ffffff"
+         offset="1"
+         id="stop756" />
+    </linearGradient>
+    <clipPath
+       clipPathUnits="userSpaceOnUse"
+       id="clipPath860">
+      <path
+         d="m 3217.535,2532.398 c -55.906,37.49 -84.611,71.163 -93.965,107.673 -6.549,25.579 -3.046,52.799 10.729,82.485 -12.371,-11.945 -26.763,-27.909 -39.52,-47.399 -17.471,-26.688 -28.504,-54.691 -32.802,-83.237 -5.36,-35.609 -0.191,-72.302 15.359,-109.072 35.383,-83.707 141.955,-212.642 235.125,-232.507 l 8.774,-1.868 -3.831,-8.107 c -40.703,-86.133 -2.786,-249.053 42.315,-327.944 19.55,-34.202 45.182,-60.327 76.181,-77.656 52.982,-29.619 111.602,-28 148.877,-22.365 -31.907,7.053 -55.635,20.762 -71.988,41.45 -34.647,43.85 -38.533,117.783 -13.401,255.199 17.496,95.648 -13.207,171.491 -74.673,184.432 l -8.82,1.86 3.871,8.139 c 26.98,56.725 -14.709,127.125 -101.381,171.208 -40.533,20.621 -73.864,39.614 -100.85,57.709 m -30.752,247.276 -20.535,-30.088 c -27.429,-40.19 -36.884,-74.856 -28.914,-105.984 12.565,-49.067 68.637,-95.794 187.491,-156.253 90.105,-45.826 134.56,-119.584 111.116,-182.084 9.405,-2.829 18.11,-6.984 26.063,-12.314 46.1,-30.909 66.872,-101.636 51.359,-186.457 -23.988,-131.175 -20.83,-204.099 10.57,-243.836 19.92,-25.204 53.224,-38.706 101.809,-41.26 l 36.38,-1.911 -34.434,-11.894 c -1.091,-0.379 -27.18,-9.273 -64.726,-11.99 -34.773,-2.531 -86.248,0.073 -133.992,26.764 -33.289,18.601 -60.736,46.533 -81.582,83.003 -24.781,43.339 -45.44,105.943 -55.277,167.464 -10.629,66.533 -7.41,124.876 8.992,165.672 -42.39,11.701 -90.738,44.514 -137.196,93.316 -42.964,45.119 -80.217,99.509 -99.656,145.493 -37.7,89.151 -10.517,161.257 18.918,206.044 31.658,48.161 71.595,75.026 73.28,76.151 z"
+         id="path858"
+         inkscape:connector-curvature="0" />
+    </clipPath>
+    <linearGradient
+       x1="0"
+       y1="0"
+       x2="1"
+       y2="0"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(616.29688,0,0,-616.29688,3045.8252,2287.2598)"
+       spreadMethod="pad"
+       id="linearGradient868">
+      <stop
+         style="stop-opacity:1;stop-color:#ffffff"
+         offset="0"
+         id="stop862" />
+      <stop
+         style="stop-opacity:1;stop-color:#ffffff"
+         offset="0.10691959"
+         id="stop864" />
+      <stop
+         style="stop-opacity:1;stop-color:#231f20"
+         offset="1"
+         id="stop866" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#blueish-6-7-5"
+       id="linearGradient12339-5"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(38.597503,0,0,38.597503,12.794587,52.903821)"
+       x1="0.30357563"
+       y1="-0.051412005"
+       x2="1"
+       y2="0"
+       spreadMethod="pad" />
+    <clipPath
+       clipPathUnits="userSpaceOnUse"
+       id="clipPath836">
+      <path
+         d="m 3287.719,2298.145 c -59.434,-102.862 -115.35,-157.371 -178.537,-178.134 -44.267,-14.548 -92.606,-11.588 -146.502,9.167 22.492,-20.371 52.302,-43.835 88.116,-64.003 49.051,-27.612 99.663,-43.747 150.431,-47.953 63.332,-5.243 127.341,8.172 190.252,39.886 143.218,72.167 357.711,275.011 381.711,441.402 l 2.26,15.669 14.73,-5.792 c 156.455,-61.525 438.868,24.431 572.474,113.136 57.917,38.451 100.897,86.663 127.765,143.287 45.914,96.784 36.16,199.816 21.852,264.786 -8.662,-57.009 -30.003,-100.412 -64.513,-131.645 -73.131,-66.17 -202.866,-81.714 -447.792,-53.649 -170.478,19.541 -300.407,-43.45 -315.954,-153.208 l -2.232,-15.757 -14.795,5.861 c -103.059,40.827 -222.109,-40.871 -289.527,-198.68 -31.534,-73.801 -61.053,-134.725 -89.739,-184.373 m -431.794,-83.271 55.395,-32.62 c 73.995,-43.561 136.159,-56.13 190.032,-38.429 84.915,27.904 160.591,132.138 253.05,348.544 70.081,164.064 194.728,251.027 307.542,217.106 3.876,16.887 10.158,32.706 18.611,47.332 49.005,84.824 171.089,129.73 322.27,112.406 233.803,-26.795 361.834,-12.641 428.108,47.326 42.046,38.045 61.898,98.28 60.668,184.132 l -0.921,64.283 25.005,-59.233 c 0.788,-1.879 19.521,-46.767 28.734,-112.562 8.542,-60.93 10.025,-151.872 -31.356,-239.096 -28.832,-60.802 -74.777,-112.418 -136.544,-153.422 -73.391,-48.735 -181.194,-92.495 -288.363,-117.053 -115.904,-26.552 -219.014,-27.759 -292.783,-3.682 -15.61,-76.019 -67.7,-165.02 -148.157,-252.569 -74.39,-80.964 -165.777,-152.97 -244.458,-192.617 -152.537,-76.88 -282.702,-37.516 -365.037,9.053 -88.526,50.066 -140.539,117.226 -142.714,120.061 z"
+         id="path834"
+         inkscape:connector-curvature="0" />
+    </clipPath>
+    <linearGradient
+       x1="0"
+       y1="0"
+       x2="1"
+       y2="0"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(12.41424,0,0,12.41424,43.304872,52.53415)"
+       spreadMethod="pad"
+       id="linearGradient844">
+      <stop
+         style="stop-opacity:1;stop-color:#ffffff"
+         offset="0"
+         id="stop838" />
+      <stop
+         style="stop-opacity:1;stop-color:#ffffff"
+         offset="0.10691959"
+         id="stop840" />
+      <stop
+         style="stop-opacity:1;stop-color:#231f20"
+         offset="1"
+         id="stop842" />
+    </linearGradient>
+    <clipPath
+       clipPathUnits="userSpaceOnUse"
+       id="clipPath1992">
+      <path
+         d="m 3311.591,5872.595 c -82.332,17.698 -131.288,43.547 -159.056,81.656 -19.451,26.7 -27.979,59.953 -25.715,100.834 -8.851,-19.611 -18.186,-44.832 -23.99,-73.387 -7.947,-39.1 -7.809,-76.762 0.406,-111.922 10.258,-43.87 33.247,-84.133 68.338,-119.667 79.877,-80.906 263.431,-181.44 380.912,-161.392 l 11.063,1.89 -0.701,-11.206 c -7.436,-118.943 112.048,-290.754 200.99,-361.568 38.553,-30.704 80.441,-49.203 124.49,-54.996 75.289,-9.895 142.674,19.121 183.391,42.925 -40.35,-6.577 -74.274,-1.626 -102.86,14.855 -60.566,34.923 -99.307,119.057 -133.706,290.409 -23.942,119.266 -94.735,193.201 -172.166,179.791 l -11.115,-1.925 0.732,11.256 c 5.101,78.417 -75.94,140.943 -197.081,152.06 -56.659,5.205 -104.189,11.846 -143.932,20.387 m -150.209,273.174 -9.936,-44.477 c -13.274,-59.407 -8.221,-104.083 15.453,-136.572 37.312,-51.21 124.118,-79.564 290.246,-94.813 125.942,-11.551 211.755,-76.704 213.439,-160.199 12.238,1.06 24.277,0.268 35.99,-2.249 67.891,-14.588 124.771,-87.169 146.004,-192.938 32.84,-163.568 70.271,-246.86 125.156,-278.51 34.829,-20.079 79.784,-20.356 137.433,-0.831 l 43.169,14.619 -34.514,-29.765 c -1.097,-0.94 -27.299,-23.36 -69.677,-43.899 -39.251,-19.03 -100.281,-39.834 -168.131,-30.92 -47.303,6.223 -92.131,25.974 -133.249,58.713 -48.855,38.906 -101.847,102.103 -141.753,169.055 -43.16,72.403 -66.426,141.703 -66.252,196.714 -54.678,-6.022 -126.065,9.736 -202.651,44.952 -70.82,32.556 -139.297,78.527 -183.178,122.975 -85.089,86.166 -86.874,182.55 -73.389,248.234 14.5,70.629 48.48,120.346 49.921,122.426 z"
+         id="path1990"
+         inkscape:connector-curvature="0" />
+    </clipPath>
+    <linearGradient
+       x1="0"
+       y1="0"
+       x2="1"
+       y2="0"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1079.04,0,0,-1079.04,3079.2959,5644.4287)"
+       spreadMethod="pad"
+       id="linearGradient2000">
+      <stop
+         style="stop-opacity:1;stop-color:#ffffff"
+         offset="0"
+         id="stop1994" />
+      <stop
+         style="stop-opacity:1;stop-color:#ffffff"
+         offset="0.10691959"
+         id="stop1996" />
+      <stop
+         style="stop-opacity:1;stop-color:#231f20"
+         offset="1"
+         id="stop1998" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient877"
+       id="linearGradient12839"
+       x1="24.9897"
+       y1="43.494854"
+       x2="112.07853"
+       y2="49.820049"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.47609494,0,0,0.33813298,9.2996261,35.777456)" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#blueish-6-7-5"
+       id="linearGradient12893"
+       x1="17.733486"
+       y1="48.622395"
+       x2="23.333635"
+       y2="48.622395"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.26778116,0,0,0.21899812,13.079481,41.415524)" />
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="3.959798"
+     inkscape:cx="184.87769"
+     inkscape:cy="915.77886"
+     inkscape:document-units="mm"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     inkscape:window-width="1920"
+     inkscape:window-height="991"
+     inkscape:window-x="0"
+     inkscape:window-y="26"
+     inkscape:window-maximized="1" />
+  <metadata
+     id="metadata11442">
+    <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></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1">
+    <rect
+       style="opacity:1;fill:#00ffff;fill-opacity:1;stroke:#9e9e9e;stroke-width:0.025;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+       id="rect11477"
+       width="146.65475"
+       height="61.988094"
+       x="3.0238097"
+       y="23.723215" />
+    <rect
+       style="opacity:1;fill:url(#linearGradient12839);fill-opacity:1.0;stroke:#9e9e9e;stroke-width:0.01003068;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+       id="rect11473"
+       width="49.989971"
+       height="3.9899693"
+       x="17.757397"
+       y="50.061562"
+       inkscape:export-xdpi="254"
+       inkscape:export-ydpi="254" />
+    <rect
+       style="opacity:1;fill:url(#linearGradient12893);fill-opacity:1;stroke:none;stroke-width:0.00605411;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+       id="rect12885"
+       width="1.4929198"
+       height="3.9939458"
+       x="17.831524"
+       y="50.066765" />
+    <path
+       inkscape:connector-curvature="0"
+       id="path810-9"
+       style="opacity:0.117;fill:url(#linearGradient12339-5);stroke:none;stroke-width:0.03704255;fill-opacity:1"
+       d="m 12.794586,52.963496 0.862722,1.016781 c 4.098478,4.603131 8.523732,-0.582855 10.794976,-3.05201 2.491852,-2.735667 5.526822,-3.58883 7.505302,-2.154691 0.191176,-0.304416 0.4221,-0.571418 0.68751,-0.800415 1.538636,-1.327161 4.236297,-1.37402 7.079609,-0.0031 4.397063,2.119907 7.001633,2.716034 8.708888,1.993371 1.083163,-0.458514 1.88295,-1.502891 2.444624,-3.192957 l 0.420506,-1.265484 0.08571,1.33079 c 0.0024,0.04215 0.06334,1.049304 -0.205067,2.400912 -0.248443,1.25189 -0.839753,3.04349 -2.245037,4.469703 -0.979923,0.994333 -2.232035,1.692104 -3.721699,2.073938 -1.770157,0.453809 -4.180218,0.575493 -6.447148,0.325456 -2.45144,-0.270262 -4.479408,-0.950142 -5.760377,-1.924954 -0.824196,1.382503 -2.451808,2.770599 -4.625132,3.936698 -2.009484,1.078493 -4.290935,1.865536 -6.102538,2.105387 -3.51267,0.46544 -5.793751,-1.193623 -7.08887,-2.667471 -1.392651,-1.584792 -1.953327,-3.255151 -1.976478,-3.325532 z"
+       sodipodi:nodetypes="cccccccccccccccccccc" />
+    <g
+       id="g966"
+       transform="matrix(0.02991036,0,0,-0.02991036,-31.177093,124.43682)"
+       style="opacity:0.056">
+      <g
+         id="g964">
+        <path
+           inkscape:connector-curvature="0"
+           id="path962"
+           style="fill:url(#linearGradient868);stroke:none"
+           d="m 3186.783,2779.674 -20.535,-30.088 c -27.429,-40.19 -36.884,-74.856 -28.914,-105.984 12.565,-49.067 68.637,-95.794 187.491,-156.253 90.105,-45.826 134.56,-119.584 111.116,-182.084 9.405,-2.829 18.11,-6.984 26.063,-12.314 46.1,-30.909 66.872,-101.636 51.359,-186.457 -23.988,-131.175 -20.83,-204.099 10.57,-243.836 19.92,-25.204 53.224,-38.706 101.809,-41.26 l 36.38,-1.911 -34.434,-11.894 c -1.091,-0.379 -27.18,-9.273 -64.726,-11.99 -34.773,-2.531 -86.248,0.073 -133.992,26.764 -33.289,18.601 -60.736,46.533 -81.582,83.003 -24.781,43.339 -45.44,105.943 -55.277,167.464 -10.629,66.533 -7.41,124.876 8.992,165.672 -42.39,11.701 -90.738,44.514 -137.196,93.316 -42.964,45.119 -80.217,99.509 -99.656,145.493 -37.7,89.151 -10.517,161.257 18.918,206.044 31.658,48.161 71.595,75.026 73.28,76.151 z"
+           sodipodi:nodetypes="ccccccccccccccccccccc" />
+      </g>
+    </g>
+    <path
+       inkscape:connector-curvature="0"
+       id="path942"
+       style="opacity:0.106;fill:url(#linearGradient844);stroke:none;stroke-width:0.00733114"
+       d="m 43.304875,55.197077 0.406108,0.239142 c 0.542468,0.319352 0.998201,0.411497 1.393151,0.281729 0.622523,-0.204568 1.177314,-0.968722 1.855144,-2.555224 0.513773,-1.202775 1.427577,-1.840313 2.254632,-1.591634 0.02841,-0.1238 0.07447,-0.239771 0.136439,-0.346996 0.359263,-0.621857 1.254277,-0.951069 2.362606,-0.824064 1.714042,0.196437 2.652654,0.09267 3.138518,-0.346954 0.308245,-0.278912 0.453782,-0.720504 0.444765,-1.349897 l -0.0067,-0.471267 0.183314,0.434245 c 0.0058,0.01377 0.143111,0.342856 0.210653,0.825208 0.06262,0.446686 0.07349,1.113394 -0.229875,1.752845 -0.211372,0.445747 -0.5482,0.824152 -1.001023,1.124758 -0.538039,0.357283 -1.328358,0.678093 -2.114028,0.858131 -0.849708,0.194656 -1.605621,0.203505 -2.146432,0.02699 -0.114438,0.557305 -0.496317,1.209784 -1.086159,1.851617 -0.545363,0.593558 -1.215333,1.121444 -1.792155,1.412102 -1.118271,0.563617 -2.072529,0.275035 -2.676138,-0.06637 -0.648995,-0.36704 -1.03031,-0.859399 -1.046255,-0.880183 z"
+       sodipodi:nodetypes="ccccccccccccccccccccc" />
+    <g
+       id="g2084"
+       transform="matrix(0.0224278,0,0,-0.0224278,-48.385178,174.7361)"
+       style="opacity:0.083">
+      <g
+         id="g2082"
+         style="">
+        <path
+           sodipodi:nodetypes="ccccccccccccccccccccc"
+           inkscape:connector-curvature="0"
+           id="path2080"
+           style="fill:url(#linearGradient2000);stroke:none"
+           d="m 3161.382,6145.769 -9.936,-44.477 c -13.274,-59.407 -8.221,-104.083 15.453,-136.572 37.312,-51.21 124.118,-79.564 290.246,-94.813 125.942,-11.551 211.755,-76.704 213.439,-160.199 12.238,1.06 24.277,0.268 35.99,-2.249 67.891,-14.588 124.771,-87.169 146.004,-192.938 32.84,-163.568 70.271,-246.86 125.156,-278.51 34.829,-20.079 79.784,-20.356 137.433,-0.831 l 43.169,14.619 -34.514,-29.765 c -1.097,-0.94 -27.299,-23.36 -69.677,-43.899 -39.251,-19.03 -100.281,-39.834 -168.131,-30.92 -47.303,6.223 -92.131,25.974 -133.249,58.713 -48.855,38.906 -101.847,102.103 -141.753,169.055 -43.16,72.403 -66.426,141.703 -66.252,196.714 -54.678,-6.022 -126.065,9.736 -202.651,44.952 -70.82,32.556 -139.297,78.527 -183.178,122.975 -85.089,86.166 -86.874,182.55 -73.389,248.234 14.5,70.629 48.48,120.346 49.921,122.426 z" />
+      </g>
+    </g>
+    <path
+       style="opacity:1;fill:#00ffff;fill-opacity:1;stroke:#9e9e9e;stroke-width:0.025;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+       d="M 4.1806233,53.981494 V 115.96943 H 150.83575 V 53.981494 Z"
+       id="rect11477-3"
+       inkscape:connector-curvature="0" />
+    <path
+       style="opacity:1;fill:#00ffff;fill-opacity:1;stroke:#9e9e9e;stroke-width:0.025;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+       d="M 5.2496761,-12.052049 V 49.935892 H 151.90481 v -61.987941 z"
+       id="rect11477-3-7"
+       inkscape:connector-curvature="0" />
+  </g>
+</svg>
diff --git a/casparcg.config b/casparcg.config
new file mode 100644 (file)
index 0000000..633eee7
--- /dev/null
@@ -0,0 +1,168 @@
+<?xml version="1.0" encoding="utf-8"?>\r
+<configuration>\r
+  <paths>\r
+    <media-path>media/</media-path>\r
+    <log-path>log/</log-path>\r
+    <data-path>data/</data-path>\r
+    <template-path>template/</template-path>\r
+    <thumbnail-path>thumbnail/</thumbnail-path>\r
+    <font-path>font/</font-path>\r
+  </paths>\r
+  <lock-clear-phrase>secret</lock-clear-phrase>\r
+  <channels>\r
+    <channel>\r
+      <video-mode>720p5994</video-mode>\r
+      <channel-layout>stereo</channel-layout>\r
+      <consumers>\r
+       <!-- <screen>\r
+          <device>1</device>\r
+          <windowed>true</windowed>\r
+        </screen> -->\r
+        <ffmpeg>\r
+          <device>1</device>\r
+          <path>unix:///tmp/caspar.sock</path>\r
+          <!-- <path>udp://localhost:5251/</path>  -->\r
+          <args>-c:v rawvideo -vf format=pix_fmts=bgra -f nut -listen 1</args>\r
+        </ffmpeg>\r
+        <system-audio></system-audio>\r
+      </consumers>\r
+    </channel>\r
+  </channels>\r
+  <controllers>\r
+    <tcp>\r
+      <port>5250</port>\r
+      <protocol>AMCP</protocol>\r
+    </tcp>\r
+    <tcp>\r
+      <port>3250</port>\r
+      <protocol>LOG</protocol>\r
+    </tcp>\r
+  </controllers>\r
+</configuration>\r
+\r
+<!--\r
+<log-level>           info  [trace|debug|info|warning|error|fatal]</log-level>\r
+<log-categories>      communication  [calltrace|communication|calltrace,communication]</log-categories>\r
+<force-deinterlace>   false  [true|false]</force-deinterlace>\r
+<channel-grid>        false [true|false]</channel-grid>\r
+<mixer>\r
+    <blend-modes>          false [true|false]</blend-modes>\r
+    <mipmapping_default_on>false [true|false]</mipmapping_default_on>\r
+    <straight-alpha>       false [true|false]</straight-alpha>\r
+</mixer>\r
+<accelerator>auto [cpu|gpu|auto]</accelerator>\r
+<template-hosts>\r
+    <template-host>\r
+        <video-mode />\r
+        <filename />\r
+        <width />\r
+        <height />\r
+    </template-host>\r
+</template-hosts>\r
+<flash>\r
+    <buffer-depth>auto [auto|1..]</buffer-depth>\r
+</flash>\r
+<html>\r
+    <remote-debugging-port>0 [0|1024-65535]</remote-debugging-port>\r
+</html>\r
+<thumbnails>\r
+    <generate-thumbnails>true [true|false]</generate-thumbnails>\r
+    <width>256</width>\r
+    <height>144</height>\r
+    <video-grid>2</video-grid>\r
+    <scan-interval-millis>5000</scan-interval-millis>\r
+    <generate-delay-millis>2000</generate-delay-millis>\r
+    <video-mode>720p2500</video-mode>\r
+    <mipmap>true</mipmap>\r
+</thumbnails>\r
+<channels>\r
+    <channel>\r
+        <video-mode>PAL [PAL|NTSC|576p2500|720p2398|720p2400|720p2500|720p5000|720p2997|720p5994|720p3000|720p6000|1080p2398|1080p2400|1080i5000|1080i5994|1080i6000|1080p2500|1080p2997|1080p3000|1080p5000|1080p5994|1080p6000|1556p2398|1556p2400|1556p2500|dci1080p2398|dci1080p2400|dci1080p2500|2160p2398|2160p2400|2160p2500|2160p2997|2160p3000|dci2160p2398|dci2160p2400|dci2160p2500] </video-mode>\r
+        <straight-alpha-output>false [true|false]</straight-alpha-output>\r
+        <channel-layout>stereo [mono|stereo|matrix|film|smpte|ebu_r123_8a|ebu_r123_8b|8ch|16ch]</channel-layout>\r
+        <consumers>\r
+            <decklink>\r
+                <device>[1..]</device>\r
+                <key-device>device + 1 [1..]</key-device>\r
+                <embedded-audio>false [true|false]</embedded-audio>\r
+                <channel-layout>stereo [mono|stereo|matrix|film|smpte|ebu_r123_8a|ebu_r123_8b|8ch|16ch]</channel-layout>\r
+                <latency>normal [normal|low|default]</latency>\r
+                <keyer>external [external|external_separate_device|internal|default]</keyer>\r
+                <key-only>false [true|false]</key-only>\r
+                <buffer-depth>3 [1..]</buffer-depth>\r
+            </decklink>\r
+            <bluefish>\r
+                <device>[1..]</device>\r
+                <embedded-audio>false [true|false]</embedded-audio>\r
+                <channel-layout>stereo [mono|stereo|matrix|film|smpte|ebu_r123_8a|ebu_r123_8b|8ch|16ch]</channel-layout>\r
+                <key-only>false [true|false]</key-only>\r
+            </bluefish>\r
+            <system-audio>\r
+                <channel-layout>stereo [mono|stereo|matrix]</channel-layout>\r
+                <latency>200 [0..]</latency>\r
+            </system-audio>\r
+            <screen>\r
+                <device>[0..]</device>\r
+                <aspect-ratio>default [default|4:3|16:9]</aspect-ratio>\r
+                <stretch>fill [none|fill|uniform|uniform_to_fill]</stretch>\r
+                <windowed>true [true|false]</windowed>\r
+                <key-only>false [true|false]</key-only>\r
+                <auto-deinterlace>true [true|false]</auto-deinterlace>\r
+                <vsync>false [true|false]</vsync>\r
+                <interactive>true [true|false]</interactive>\r
+                <borderless>false [true|false]</borderless>\r
+            </screen>\r
+            <newtek-ivga></newtek-ivga>\r
+            <ffmpeg>\r
+                <path>[file|url]</path>\r
+                <args>[most ffmpeg arguments related to filtering and output codecs]</args>\r
+                <separate-key>false [true|false]</separate-key>\r
+                <mono-streams>false [true|false]</mono-streams>\r
+            </ffmpeg>\r
+            <syncto>\r
+                <channel-id>1</channel-id>\r
+            </syncto>\r
+        </consumers>\r
+    </channel>\r
+</channels>\r
+<osc>\r
+  <default-port>6250</default-port>\r
+  <disable-send-to-amcp-clients>false [true|false]</disable-send-to-amcp-clients>\r
+  <predefined-clients>\r
+    <predefined-client>\r
+      <address>127.0.0.1</address>\r
+      <port>5253</port>\r
+    </predefined-client>\r
+  </predefined-clients>\r
+</osc>\r
+<audio>\r
+       <channel-layouts>\r
+               <channel-layout name="mono"        type="mono"        num-channels="1" channel-order="FC" />\r
+               <channel-layout name="stereo"      type="stereo"      num-channels="2" channel-order="FL FR" />\r
+               <channel-layout name="matrix"      type="matrix"      num-channels="2" channel-order="ML MR" />\r
+               <channel-layout name="film"        type="5.1"         num-channels="6" channel-order="FL FC FR BL BR LFE" />\r
+               <channel-layout name="smpte"       type="5.1"         num-channels="6" channel-order="FL FR FC LFE BL BR" />\r
+               <channel-layout name="ebu_r123_8a" type="5.1+downmix" num-channels="8" channel-order="DL DR FL FR FC LFE BL BR" />\r
+               <channel-layout name="ebu_r123_8b" type="5.1+downmix" num-channels="8" channel-order="FL FR FC LFE BL BR DL DR" />\r
+               <channel-layout name="8ch"         type="8ch"         num-channels="8" />\r
+               <channel-layout name="16ch"        type="16ch"        num-channels="16" />\r
+       </channel-layouts>\r
+       <mix-configs>\r
+               <mix-config from-type="mono"          to-types="stereo, 5.1"  mix="FL = FC                                           | FR = FC" />\r
+               <mix-config from-type="mono"          to-types="5.1+downmix"  mix="FL = FC                                           | FR = FC                                         | DL = FC | DR = FC" />\r
+               <mix-config from-type="mono"          to-types="matrix"       mix="ML = FC                                           | MR = FC" />\r
+               <mix-config from-type="stereo"        to-types="mono"         mix="FC &lt; FL + FR" />\r
+               <mix-config from-type="stereo"        to-types="matrix"       mix="ML = FL                                           | MR = FR" />\r
+               <mix-config from-type="stereo"        to-types="5.1"          mix="FL = FL                                           | FR = FR" />\r
+               <mix-config from-type="stereo"        to-types="5.1+downmix"  mix="FL = FL                                           | FR = FR                                         | DL = FL | DR = FR" />\r
+               <mix-config from-type="5.1"           to-types="mono"         mix="FC &lt; FL + FR + 0.707*FC + 0.707*BL + 0.707*BR" />\r
+               <mix-config from-type="5.1"           to-types="stereo"       mix="FL &lt; FL + 0.707*FC + 0.707*BL                  | FR &lt; FR + 0.707*FC + 0.707*BR" />\r
+               <mix-config from-type="5.1"           to-types="5.1+downmix"  mix="FL = FL                                           | FR = FR                                         | FC = FC | BL = BL | BR = BR | LFE = LFE | DL &lt; FL + 0.707*FC + 0.707*BL | DR &lt; FR + 0.707*FC + 0.707*BR" />\r
+               <mix-config from-type="5.1"           to-types="matrix"       mix="ML = 0.3204*FL + 0.293*FC + -0.293*BL + -0.293*BR | MR = 0.3204*FR + 0.293*FC + 0.293*BL + 0.293*BR" />\r
+               <mix-config from-type="5.1+stereomix" to-types="mono"         mix="FC &lt; DL + DR" />\r
+               <mix-config from-type="5.1+stereomix" to-types="stereo"       mix="FL = DL                                           | FR = DR" />\r
+               <mix-config from-type="5.1+stereomix" to-types="5.1"          mix="FL = FL                                           | FR = FR                                         | FC = FC | BL = BL | BR = BR | LFE = LFE" />\r
+               <mix-config from-type="5.1+stereomix" to-types="matrix"       mix="ML = 0.3204*FL + 0.293*FC + -0.293*BL + -0.293*BR | MR = 0.3204*FR + 0.293*FC + 0.293*BL + 0.293*BR" />\r
+       </mix-configs>\r
+</audio>\r
+-->\r
diff --git a/client/Makefile b/client/Makefile
new file mode 100644 (file)
index 0000000..e76e5b8
--- /dev/null
@@ -0,0 +1,30 @@
+CXX=g++
+PROTOC=protoc
+INSTALL=install
+EMBEDDED_BMUSB=no
+PKG_MODULES := Qt5Core Qt5Gui Qt5Widgets
+CXXFLAGS ?= -O2 -g -Wall  # Will be overridden by environment.
+CXXFLAGS += -std=gnu++11 -fPIC $(shell pkg-config --cflags $(PKG_MODULES)) -pthread
+LDLIBS=$(shell pkg-config --libs $(PKG_MODULES)) -pthread
+
+OBJS_WITH_MOC = mainwindow.o
+OBJS += $(OBJS_WITH_MOC) main.o acmp_client.o
+OBJS += $(OBJS_WITH_MOC:.o=.moc.o)
+
+%.o: %.cpp
+       $(CXX) -MMD -MP $(CPPFLAGS) $(CXXFLAGS) -o $@ -c $<
+%.o: %.cc
+       $(CXX) -MMD -MP $(CPPFLAGS) $(CXXFLAGS) -o $@ -c $<
+
+ui_%.h: %.ui
+       uic $< -o $@
+
+%.moc.cpp: %.h
+       moc $< -o $@
+
+all: ultimatescore
+
+ultimatescore: $(OBJS)
+       $(CXX) -o $@ $^ $(LDFLAGS) $(LDLIBS)
+
+mainwindow.o: ui_mainwindow.h
diff --git a/client/acmp_client.cpp b/client/acmp_client.cpp
new file mode 100644 (file)
index 0000000..bb1e56c
--- /dev/null
@@ -0,0 +1,213 @@
+#include "acmp_client.h"
+
+#include <functional>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/ioctl.h>
+
+using namespace std;
+
+ACMPClient::ACMPClient(const string &host, int port)
+       : host(host), port(port) {}
+
+void ACMPClient::add_init_command(const string &cmd)
+{
+       init_commands.push_back(cmd + "\r\n");
+}
+
+void ACMPClient::start()
+{
+       t = thread(&ACMPClient::thread_func, this);
+}
+
+void ACMPClient::end()
+{
+       t.join();
+}
+
+void ACMPClient::send_command(const string &cmd)
+{
+       lock_guard<mutex> lock(mu);
+       queued_commands.push_back(cmd + "\r\n");
+}
+
+void ACMPClient::change_server(const string &host, int port)
+{
+       lock_guard<mutex> lock(mu);
+       queued_commands.push_back("");  // Marker for disconnect.
+       this->host = host;
+       this->port = port;
+}
+
+void ACMPClient::set_connection_callback(const std::function<void(bool)> &callback)
+{
+       connection_callback = callback; 
+}
+
+namespace {
+
+int lookup_and_connect(const char *host, int port)
+{
+       addrinfo hints;
+       memset(&hints, 0, sizeof(hints));
+       hints.ai_family = AF_UNSPEC;
+       hints.ai_socktype = SOCK_STREAM;
+
+       addrinfo *res;
+       char portstr[16];
+       snprintf(portstr, sizeof(portstr), "%d", port);
+       int err = getaddrinfo(host, portstr, &hints, &res);
+       if (err != 0) {
+               fprintf(stderr, "Lookup of %s:%d failed: %s\n", host, port, strerror(errno));
+               return -1;
+       }
+
+       for (addrinfo *p = res; p != NULL; p = p->ai_next) {
+               int sock = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
+               if (sock == -1) {
+                       perror("socket");
+                       continue;
+               }
+
+               if (connect(sock, p->ai_addr, p->ai_addrlen) == -1) {
+                       perror("connect");
+                       close(sock);
+                       continue;
+               }
+
+               // Success!
+               freeaddrinfo(res);
+               return sock;
+       }
+
+       freeaddrinfo(res);
+       return -1;
+}
+
+}  // namespace
+
+void ACMPClient::thread_func()
+{
+       if (connection_callback) {
+               connection_callback(false);
+       }
+       for ( ;; ) {
+               int sock, port_copy;
+               string host_copy;
+               {
+                       lock_guard<mutex> lock(mu);
+                       host_copy = host;
+                       port_copy = port;
+               }
+               sock = lookup_and_connect(host_copy.c_str(), port_copy);
+               if (sock == -1) {
+                       sleep(1);
+                       continue;
+               }
+
+               int one = 1;
+               if (ioctl(sock, FIONBIO, &one) == 1) {
+                       perror("ioctl(FIONBIO)");
+                       close(sock);
+                       sleep(1);
+                       continue;
+               }
+
+               printf("Connected to CasparCG.\n");
+               if (connection_callback) {
+                       connection_callback(true);
+               }
+
+               bool first = true;
+
+               for ( ;; ) {
+                       vector<string> commands;
+                       if (first) {
+                               commands = init_commands;
+                               first = false;
+                       } else {
+                               lock_guard<mutex> lock(mu);
+                               swap(commands, queued_commands);
+                       }
+
+                       bool broken = false;
+                       string buf;
+                       for (const string &cmd : commands) {
+                               buf += cmd;
+                               if (cmd.empty()) {
+                                       printf("Closing CasparCG socket for reconnection.\n");
+                                       broken = true;
+                                       break;
+                               }
+                       }
+
+                       if (broken) {
+                               break;
+                       }
+                       if (!buf.empty()) {
+                               printf("Writing: '%s'\n", buf.c_str());
+                       }
+
+                       size_t pos = 0;
+                       do {
+                               // Consume until there is no more.
+                               char junk[1024];
+                               int err = read(sock, junk, sizeof(junk));
+                               if (err == -1) {
+                                       if (err == EAGAIN) {
+                                               perror("read");
+                                               broken = true;
+                                               break;
+                                       }
+                               }
+                               if (err == 0) {
+                                       // Closed.
+                                       printf("Server closed connection.\n");
+                                       broken = true;
+                                       break;
+                               }
+                               if (err > 0) {
+                                       // Try again.
+                                       junk[err] = 0;
+                                       printf("From server: '%s'\n", junk);
+                                       continue;
+                               }
+
+                               if (pos < buf.size()) {
+                                       // Now write as much as we can.
+                                       err = write(sock, buf.data() + pos, buf.size() - pos);
+                                       if (err == -1) {
+                                               perror("write");
+                                               broken = true;
+                                               break;
+                                       }
+                                       if (err == 0) {
+                                               // Uh-oh. Buffer full for some reason?
+                                               usleep(10000);
+                                       }
+                                       pos += err;
+                               }
+                       } while (pos < buf.size());
+
+                       if (broken) {
+                               break;
+                       }
+                       if (buf.empty()) {
+                               usleep(100000);
+                               continue;
+                       }
+               }
+
+               close(sock);
+               if (connection_callback) {
+                       connection_callback(false);
+               }
+               sleep(1);
+       }
+}
diff --git a/client/acmp_client.h b/client/acmp_client.h
new file mode 100644 (file)
index 0000000..a386b27
--- /dev/null
@@ -0,0 +1,34 @@
+#ifndef _ACMP_CLIENT_H
+#define _ACMP_CLIENT_H 1
+
+#include <functional>
+#include <mutex>
+#include <string>
+#include <thread>
+#include <vector>
+
+class ACMPClient {
+public:
+       ACMPClient(const std::string &host, int port);
+
+       void add_init_command(const std::string &cmd);
+       void start();
+       void end();
+       void send_command(const std::string &cmd);  // Thread-safe.
+       void change_server(const std::string &host, int port);  // Thread-safe.
+       void set_connection_callback(const std::function<void(bool)> &callback);
+
+private:
+       void thread_func();
+
+       std::thread t;
+       std::vector<std::string> init_commands;
+       std::function<void(bool)> connection_callback;
+
+       std::mutex mu;
+       std::string host;  // Protected by mu.
+       int port;  // Protected by mu.
+       std::vector<std::string> queued_commands;  // Protected by mu.
+};
+
+#endif  // !defined(_ACMP_CLIENT_H)
diff --git a/client/main.cpp b/client/main.cpp
new file mode 100644 (file)
index 0000000..edd02fc
--- /dev/null
@@ -0,0 +1,12 @@
+#include "acmp_client.h"
+#include "mainwindow.h"
+#include <QApplication>
+
+int main(int argc, char *argv[])
+{
+       QApplication a(argc, argv);
+       MainWindow w;
+       w.show();
+
+       return a.exec();
+}
diff --git a/client/mainwindow.cpp b/client/mainwindow.cpp
new file mode 100644 (file)
index 0000000..3dd3715
--- /dev/null
@@ -0,0 +1,262 @@
+#include "mainwindow.h"
+#include "post_to_main_thread.h"
+#include "ui_mainwindow.h"
+
+#include <stdlib.h>
+
+using namespace std;
+
+string escape_html(const string &str)
+{
+       string s = "";
+       for (char ch : str) {
+               if (ch == '<') {
+                       s += "&lt;";
+               } else if (ch == '>') {
+                       s += "&gt;";
+               } else if (ch == '&') {
+                       s += "&amp;";
+               } else {
+                       s += ch;
+               }
+       }
+       return s;
+}
+
+string escape_unicode(const string &str)
+{
+       string s = "";
+       for (size_t pos = 0; pos < str.size(); ) {
+               wchar_t wc;
+               int len = mbtowc(&wc, str.data() + pos, str.size() - pos);
+               if (len == -1) {
+                       wc = '?';
+                       len = 1;
+               }
+               pos += len;
+
+               if (wc == '\\') {
+                       s += "\\\\";
+               } else if (isprint(wc)) {
+                       s += wc;
+               } else {
+                       char buf[16];
+                       snprintf(buf, sizeof(buf), "\\u%04x", wc);
+                       s += buf;
+               }
+       }
+       return s;
+}
+
+string escape_quotes(const string &str)
+{
+       string s = "";
+       for (char ch : str) {
+               if (ch == '"' || ch == '\\') {
+                       s += '\\';
+               }
+               s += ch;
+       }
+       return s;
+}
+
+string serialize_as_json(const map<string, string> &param)
+{
+       string s = "{";
+
+       bool first = true;
+       for (const auto &key_value : param) {
+               if (!first) s += ", ";
+               first = false;
+
+               s += '"';
+               s += escape_quotes(escape_unicode(key_value.first));
+               s += "\": \"";
+               s += escape_quotes(escape_unicode(key_value.second));
+               s += '"';
+       }
+       s += "}";
+       return s;       
+}
+
+MainWindow::MainWindow(QWidget *parent) :
+    QMainWindow(parent),
+    ui(new Ui::MainWindow)
+{
+       ui->setupUi(this);
+       acmp = new ACMPClient("127.0.0.1", 5250);
+       acmp->add_init_command("MIXER 1 STRAIGHT_ALPHA_OUTPUT 1");
+       acmp->add_init_command("CG 1 ADD 0 \"score\" 1 \"<templateData></templateData>\"");
+       acmp->set_connection_callback([this](bool connected) {
+               string msg = connected ? "Connected" : "Not connected";
+               post_to_main_thread([this, msg]() {
+                       ui->casparcg_connected_label->setText(QString::fromStdString(msg));
+               });
+       });
+
+       acmp->start();
+
+       connect(ui->casparcg_reconnect_btn, &QPushButton::clicked, this, &MainWindow::casparcg_reconnect_clicked);
+       connect(ui->set_initials_btn, &QPushButton::clicked, this, &MainWindow::set_initials_clicked);
+       connect(ui->set_color_btn, &QPushButton::clicked, this, &MainWindow::set_color_clicked);
+       connect(ui->set_score_btn, &QPushButton::clicked, this, &MainWindow::set_score_clicked);
+       connect(ui->set_all_scorebug_btn, &QPushButton::clicked, this, &MainWindow::set_all_scorebug_clicked);
+       connect(ui->goal_1_btn, &QPushButton::clicked, this, [this]() { add_goal(ui->score_1_box, 1); });
+       connect(ui->ungoal_1_btn, &QPushButton::clicked, this, [this]() { add_goal(ui->score_1_box, -1); });
+       connect(ui->goal_2_btn, &QPushButton::clicked, this, [this]() { add_goal(ui->score_2_box, 1); });
+       connect(ui->ungoal_2_btn, &QPushButton::clicked, this, [this]() { add_goal(ui->score_2_box, -1); });
+
+       connect(ui->set_clock_btn, &QPushButton::clicked, this, &MainWindow::set_clock_clicked);
+       connect(ui->start_and_show_clock_btn, &QPushButton::clicked, this, &MainWindow::start_and_show_clock_clicked);
+       connect(ui->stop_clock_btn, &QPushButton::clicked, this, &MainWindow::stop_clock_clicked);
+       connect(ui->show_clock_btn, &QPushButton::clicked, this, &MainWindow::show_clock_clicked);
+       connect(ui->hide_clock_btn, &QPushButton::clicked, this, &MainWindow::hide_clock_clicked);
+
+       connect(ui->set_comment_btn, &QPushButton::clicked, this, &MainWindow::set_comment_clicked);
+       connect(ui->set_and_show_comment_btn, &QPushButton::clicked, this, &MainWindow::set_and_show_comment_clicked);
+       connect(ui->hide_comment_btn, &QPushButton::clicked, this, &MainWindow::hide_comment_clicked);
+       connect(ui->set_and_show_autocomment_btn, &QPushButton::clicked, this, &MainWindow::set_and_show_autocomment_clicked);
+
+       connect(ui->show_lower_third_btn, &QPushButton::clicked, this, &MainWindow::show_lower_third_clicked);
+       connect(ui->hide_lower_third_btn, &QPushButton::clicked, this, &MainWindow::hide_lower_third_clicked);
+
+       autocomment_update();
+}
+
+MainWindow::~MainWindow()
+{
+       acmp->end();
+       delete ui;
+}
+
+void MainWindow::casparcg_reconnect_clicked()
+{
+       acmp->change_server(ui->casparcg_host_box->text().toStdString(),
+                           stoi(ui->casparcg_port_box->text().toStdString()));
+}
+
+void MainWindow::set_initials_clicked()
+{
+       map<string, string> param;
+       param["team1"] = escape_html(ui->initials_1_edit->text().toStdString());
+       param["team2"] = escape_html(ui->initials_2_edit->text().toStdString());
+       acmp->send_command("cg 1 update 1 \"" + escape_quotes(serialize_as_json(param)) + "\"");
+       acmp->send_command("cg 1 invoke 1 setteams");
+}
+
+void MainWindow::set_color_clicked()
+{
+       map<string, string> param;
+       param["team1color"] = ui->color_1_edit->text().toStdString();  // Should maybe be escaped, but meh.
+       param["team2color"] = ui->color_2_edit->text().toStdString();
+       acmp->send_command("cg 1 update 1 \"" + escape_quotes(serialize_as_json(param)) + "\"");
+       acmp->send_command("cg 1 invoke 1 setcolors");
+}
+
+void MainWindow::set_score_clicked()
+{
+       map<string, string> param;
+       param["score1"] = to_string(ui->score_1_box->value());
+       param["score2"] = to_string(ui->score_2_box->value());
+       acmp->send_command("cg 1 update 1 \"" + escape_quotes(serialize_as_json(param)) + "\"");
+       acmp->send_command("cg 1 invoke 1 setscore");
+       autocomment_update();
+}
+
+void MainWindow::set_all_scorebug_clicked()
+{
+       set_initials_clicked();
+       set_color_clicked();
+       set_score_clicked();
+}
+
+void MainWindow::add_goal(QSpinBox *box, int delta)
+{
+       box->setValue(box->value() + delta);
+       set_score_clicked();
+}
+
+void MainWindow::set_clock_clicked()
+{
+       map<string, string> param;
+       param["clock_min"] = to_string(ui->clock_min_box->value());
+       param["clock_sec"] = to_string(ui->clock_sec_box->value());
+       acmp->send_command("cg 1 update 1 \"" + escape_quotes(serialize_as_json(param)) + "\"");
+       acmp->send_command("cg 1 invoke 1 setclockfromstate");
+}
+
+void MainWindow::start_and_show_clock_clicked()
+{
+       acmp->send_command("cg 1 invoke 1 startclock");  // Also shows.
+}
+
+void MainWindow::stop_clock_clicked()
+{
+       acmp->send_command("cg 1 invoke 1 stopclock");
+}
+
+void MainWindow::show_clock_clicked()
+{
+       acmp->send_command("cg 1 invoke 1 showclock");
+}
+
+void MainWindow::hide_clock_clicked()
+{
+       acmp->send_command("cg 1 invoke 1 hideclock");
+}
+
+void MainWindow::set_comment_clicked()
+{
+       map<string, string> param;
+       param["comment"] = ui->comment_edit->text().toStdString();
+       acmp->send_command("cg 1 update 1 \"" + escape_quotes(serialize_as_json(param)) + "\"");
+       acmp->send_command("cg 1 invoke 1 setcomment");
+}
+
+void MainWindow::set_and_show_comment_clicked()
+{
+       set_comment_clicked();
+       acmp->send_command("cg 1 invoke 1 showcomment");
+}
+
+void MainWindow::set_and_show_autocomment_clicked()
+{
+       ui->comment_edit->setText(ui->autocomment_edit->text());
+       set_and_show_comment_clicked();
+}
+
+void MainWindow::hide_comment_clicked()
+{
+       acmp->send_command("cg 1 invoke 1 hidecomment");
+}
+
+void MainWindow::show_lower_third_clicked()
+{
+       map<string, string> param;
+       param["text1"] = ui->lowerthird_heading_edit->text().toStdString();
+       param["text2"] = ui->lowerthird_subheading_edit->text().toStdString();
+       acmp->send_command("cg 1 update 1 \"" + escape_quotes(serialize_as_json(param)) + "\"");
+       acmp->send_command("cg 1 invoke 1 setandshowlowerthird");
+}
+
+void MainWindow::hide_lower_third_clicked()
+{
+       acmp->send_command("cg 1 invoke 1 hidelowerthird");
+}
+
+void MainWindow::autocomment_update()
+{
+       int score1 = ui->score_1_box->value();
+       int score2 = ui->score_2_box->value();
+       string msg;
+       if (abs(score1 - score2) >= 3) {
+               msg = "Game ends after this point";
+       } else if (score1 >= 12 || score2 >= 12) {
+               msg = "Point cap: First to 13";
+       } else {
+               char buf[32];
+               snprintf(buf, sizeof(buf), "Pagacap: First to %d", max(score1, score2) + 1);
+               msg = buf;
+       }
+       ui->autocomment_edit->setText(QString::fromStdString(msg));
+}
diff --git a/client/mainwindow.h b/client/mainwindow.h
new file mode 100644 (file)
index 0000000..f2b0a67
--- /dev/null
@@ -0,0 +1,46 @@
+#ifndef MAINWINDOW_H
+#define MAINWINDOW_H
+
+#include <QMainWindow>
+
+#include "acmp_client.h"
+
+namespace Ui {
+class MainWindow;
+}
+
+class QSpinBox;
+
+class MainWindow : public QMainWindow
+{
+Q_OBJECT
+
+public:
+       explicit MainWindow(QWidget *parent = 0);
+       ~MainWindow();
+
+private:
+       void casparcg_reconnect_clicked();
+       void set_initials_clicked();
+       void set_color_clicked();
+       void set_score_clicked();
+       void set_all_scorebug_clicked();
+       void add_goal(QSpinBox *box, int delta);
+       void set_clock_clicked();
+       void start_and_show_clock_clicked();
+       void stop_clock_clicked();
+       void show_clock_clicked();
+       void hide_clock_clicked();
+       void set_comment_clicked();
+       void set_and_show_comment_clicked();
+       void hide_comment_clicked();
+       void set_and_show_autocomment_clicked();
+       void show_lower_third_clicked();
+       void hide_lower_third_clicked();
+       void autocomment_update();
+
+       Ui::MainWindow *ui;
+       ACMPClient *acmp;
+};
+
+#endif // MAINWINDOW_H
diff --git a/client/mainwindow.ui b/client/mainwindow.ui
new file mode 100644 (file)
index 0000000..d512948
--- /dev/null
@@ -0,0 +1,459 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>MainWindow</class>
+ <widget class="QMainWindow" name="MainWindow">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>720</width>
+    <height>552</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>MainWindow</string>
+  </property>
+  <widget class="QWidget" name="centralWidget">
+   <layout class="QVBoxLayout" name="verticalLayout_2">
+    <item>
+     <layout class="QVBoxLayout" name="verticalLayout">
+      <property name="leftMargin">
+       <number>6</number>
+      </property>
+      <property name="topMargin">
+       <number>6</number>
+      </property>
+      <property name="rightMargin">
+       <number>6</number>
+      </property>
+      <property name="bottomMargin">
+       <number>6</number>
+      </property>
+      <item>
+       <layout class="QHBoxLayout" name="horizontalLayout_10">
+        <item>
+         <widget class="QLabel" name="label_11">
+          <property name="text">
+           <string>CasparCG server:</string>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QLineEdit" name="casparcg_host_box">
+          <property name="text">
+           <string>localhost</string>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QLineEdit" name="casparcg_port_box">
+          <property name="maximumSize">
+           <size>
+            <width>50</width>
+            <height>16777215</height>
+           </size>
+          </property>
+          <property name="text">
+           <string>5250</string>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QPushButton" name="casparcg_reconnect_btn">
+          <property name="text">
+           <string>Reconnect</string>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QLabel" name="casparcg_connected_label">
+          <property name="minimumSize">
+           <size>
+            <width>100</width>
+            <height>0</height>
+           </size>
+          </property>
+          <property name="text">
+           <string>Not connected</string>
+          </property>
+          <property name="alignment">
+           <set>Qt::AlignCenter</set>
+          </property>
+         </widget>
+        </item>
+       </layout>
+      </item>
+      <item>
+       <layout class="QHBoxLayout" name="horizontalLayout_3">
+        <item>
+         <layout class="QGridLayout" name="gridLayout">
+          <item row="4" column="2">
+           <layout class="QHBoxLayout" name="horizontalLayout_4">
+            <item>
+             <widget class="QPushButton" name="goal_2_btn">
+              <property name="text">
+               <string>+1 point</string>
+              </property>
+             </widget>
+            </item>
+            <item>
+             <widget class="QPushButton" name="ungoal_2_btn">
+              <property name="text">
+               <string>-1 point</string>
+              </property>
+             </widget>
+            </item>
+           </layout>
+          </item>
+          <item row="3" column="0">
+           <widget class="QLabel" name="label_6">
+            <property name="text">
+             <string>Score</string>
+            </property>
+           </widget>
+          </item>
+          <item row="3" column="2">
+           <widget class="QSpinBox" name="score_2_box"/>
+          </item>
+          <item row="3" column="3">
+           <widget class="QPushButton" name="set_score_btn">
+            <property name="text">
+             <string>Set</string>
+            </property>
+           </widget>
+          </item>
+          <item row="2" column="0">
+           <widget class="QLabel" name="label_7">
+            <property name="text">
+             <string>CSS color</string>
+            </property>
+           </widget>
+          </item>
+          <item row="1" column="1">
+           <widget class="QLineEdit" name="initials_1_edit">
+            <property name="text">
+             <string>PCL</string>
+            </property>
+           </widget>
+          </item>
+          <item row="4" column="1">
+           <layout class="QHBoxLayout" name="horizontalLayout_5">
+            <item>
+             <widget class="QPushButton" name="goal_1_btn">
+              <property name="text">
+               <string>+1 point</string>
+              </property>
+             </widget>
+            </item>
+            <item>
+             <widget class="QPushButton" name="ungoal_1_btn">
+              <property name="text">
+               <string>-1 point</string>
+              </property>
+             </widget>
+            </item>
+           </layout>
+          </item>
+          <item row="2" column="1">
+           <widget class="QLineEdit" name="color_1_edit">
+            <property name="text">
+             <string>red</string>
+            </property>
+           </widget>
+          </item>
+          <item row="3" column="1">
+           <widget class="QSpinBox" name="score_1_box"/>
+          </item>
+          <item row="0" column="2">
+           <widget class="QLabel" name="label_4">
+            <property name="text">
+             <string>Team 2</string>
+            </property>
+           </widget>
+          </item>
+          <item row="0" column="1">
+           <widget class="QLabel" name="label_3">
+            <property name="text">
+             <string>Team 1</string>
+            </property>
+           </widget>
+          </item>
+          <item row="1" column="2">
+           <widget class="QLineEdit" name="initials_2_edit">
+            <property name="text">
+             <string>TFK</string>
+            </property>
+           </widget>
+          </item>
+          <item row="1" column="0">
+           <widget class="QLabel" name="label_5">
+            <property name="text">
+             <string>Initials</string>
+            </property>
+           </widget>
+          </item>
+          <item row="1" column="3">
+           <widget class="QPushButton" name="set_initials_btn">
+            <property name="text">
+             <string>Set</string>
+            </property>
+           </widget>
+          </item>
+          <item row="2" column="2">
+           <widget class="QLineEdit" name="color_2_edit">
+            <property name="text">
+             <string>yellow</string>
+            </property>
+           </widget>
+          </item>
+          <item row="2" column="3">
+           <widget class="QPushButton" name="set_color_btn">
+            <property name="text">
+             <string>Set</string>
+            </property>
+           </widget>
+          </item>
+          <item row="4" column="3">
+           <widget class="QPushButton" name="set_all_scorebug_btn">
+            <property name="text">
+             <string>Set all</string>
+            </property>
+           </widget>
+          </item>
+         </layout>
+        </item>
+       </layout>
+      </item>
+      <item>
+       <widget class="Line" name="line_3">
+        <property name="orientation">
+         <enum>Qt::Horizontal</enum>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <layout class="QHBoxLayout" name="horizontalLayout_2">
+        <item>
+         <widget class="QLabel" name="label">
+          <property name="text">
+           <string>Set clock to:</string>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QSpinBox" name="clock_min_box">
+          <property name="sizePolicy">
+           <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+            <horstretch>0</horstretch>
+            <verstretch>0</verstretch>
+           </sizepolicy>
+          </property>
+          <property name="value">
+           <number>25</number>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QLabel" name="label_2">
+          <property name="sizePolicy">
+           <sizepolicy hsizetype="Fixed" vsizetype="Preferred">
+            <horstretch>0</horstretch>
+            <verstretch>0</verstretch>
+           </sizepolicy>
+          </property>
+          <property name="text">
+           <string>:</string>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QSpinBox" name="clock_sec_box">
+          <property name="sizePolicy">
+           <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+            <horstretch>0</horstretch>
+            <verstretch>0</verstretch>
+           </sizepolicy>
+          </property>
+          <property name="maximum">
+           <number>59</number>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QPushButton" name="set_clock_btn">
+          <property name="text">
+           <string>Set</string>
+          </property>
+         </widget>
+        </item>
+       </layout>
+      </item>
+      <item>
+       <layout class="QHBoxLayout" name="horizontalLayout">
+        <item>
+         <widget class="QPushButton" name="start_and_show_clock_btn">
+          <property name="text">
+           <string>Start (and show) clock</string>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QPushButton" name="stop_clock_btn">
+          <property name="text">
+           <string>Stop clock</string>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QPushButton" name="show_clock_btn">
+          <property name="text">
+           <string>Show clock</string>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QPushButton" name="hide_clock_btn">
+          <property name="text">
+           <string>Hide clock</string>
+          </property>
+         </widget>
+        </item>
+       </layout>
+      </item>
+      <item>
+       <widget class="Line" name="line_2">
+        <property name="orientation">
+         <enum>Qt::Horizontal</enum>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <layout class="QHBoxLayout" name="horizontalLayout_6">
+        <item>
+         <widget class="QLabel" name="label_8">
+          <property name="text">
+           <string>Comment:</string>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QLineEdit" name="comment_edit">
+          <property name="text">
+           <string>Pagacap: First to 9 points</string>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QPushButton" name="set_comment_btn">
+          <property name="text">
+           <string>Set</string>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QPushButton" name="set_and_show_comment_btn">
+          <property name="text">
+           <string>Set+show</string>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QPushButton" name="hide_comment_btn">
+          <property name="text">
+           <string>Hide</string>
+          </property>
+         </widget>
+        </item>
+       </layout>
+      </item>
+      <item>
+       <layout class="QHBoxLayout" name="horizontalLayout_11">
+        <item>
+         <widget class="QLabel" name="label_12">
+          <property name="text">
+           <string>Suggested autocomment if game ends right now:</string>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QLineEdit" name="autocomment_edit">
+          <property name="enabled">
+           <bool>true</bool>
+          </property>
+          <property name="text">
+           <string/>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QPushButton" name="set_and_show_autocomment_btn">
+          <property name="text">
+           <string>Set+show</string>
+          </property>
+         </widget>
+        </item>
+       </layout>
+      </item>
+      <item>
+       <widget class="Line" name="line">
+        <property name="orientation">
+         <enum>Qt::Horizontal</enum>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <layout class="QHBoxLayout" name="horizontalLayout_7">
+        <item>
+         <widget class="QLabel" name="label_9">
+          <property name="text">
+           <string>Lower third heading (HTML allowed):</string>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QLineEdit" name="lowerthird_heading_edit"/>
+        </item>
+       </layout>
+      </item>
+      <item>
+       <layout class="QHBoxLayout" name="horizontalLayout_8">
+        <item>
+         <widget class="QLabel" name="label_10">
+          <property name="text">
+           <string>Lower third subheading (HTML allowed):</string>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QLineEdit" name="lowerthird_subheading_edit"/>
+        </item>
+       </layout>
+      </item>
+      <item>
+       <layout class="QHBoxLayout" name="horizontalLayout_9">
+        <item>
+         <widget class="QPushButton" name="show_lower_third_btn">
+          <property name="text">
+           <string>Set + show lower third</string>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QPushButton" name="hide_lower_third_btn">
+          <property name="text">
+           <string>Hide lower third</string>
+          </property>
+         </widget>
+        </item>
+       </layout>
+      </item>
+     </layout>
+    </item>
+   </layout>
+  </widget>
+  <widget class="QStatusBar" name="statusBar"/>
+ </widget>
+ <layoutdefault spacing="6" margin="11"/>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/client/post_to_main_thread.h b/client/post_to_main_thread.h
new file mode 100644 (file)
index 0000000..0462c7b
--- /dev/null
@@ -0,0 +1,16 @@
+#ifndef _POST_TO_MAIN_THREAD_H
+#define _POST_TO_MAIN_THREAD_H 1
+
+#include <QApplication>
+#include <QObject>
+#include <memory>
+
+// http://stackoverflow.com/questions/21646467/how-to-execute-a-functor-in-a-given-thread-in-qt-gcd-style
+template<typename F>
+static inline void post_to_main_thread(F &&fun)
+{
+       QObject signalSource;
+       QObject::connect(&signalSource, &QObject::destroyed, qApp, std::move(fun));
+}
+
+#endif  // !defined(_POST_TO_MAIN_THREAD_H)
diff --git a/client/ui_mainwindow.h b/client/ui_mainwindow.h
new file mode 100644 (file)
index 0000000..4732e1e
--- /dev/null
@@ -0,0 +1,529 @@
+/********************************************************************************
+** Form generated from reading UI file 'mainwindow.ui'
+**
+** Created by: Qt User Interface Compiler version 5.7.1
+**
+** WARNING! All changes made in this file will be lost when recompiling UI file!
+********************************************************************************/
+
+#ifndef UI_MAINWINDOW_H
+#define UI_MAINWINDOW_H
+
+#include <QtCore/QVariant>
+#include <QtWidgets/QAction>
+#include <QtWidgets/QApplication>
+#include <QtWidgets/QButtonGroup>
+#include <QtWidgets/QFrame>
+#include <QtWidgets/QGridLayout>
+#include <QtWidgets/QHBoxLayout>
+#include <QtWidgets/QHeaderView>
+#include <QtWidgets/QLabel>
+#include <QtWidgets/QLineEdit>
+#include <QtWidgets/QMainWindow>
+#include <QtWidgets/QPushButton>
+#include <QtWidgets/QSpinBox>
+#include <QtWidgets/QStatusBar>
+#include <QtWidgets/QVBoxLayout>
+#include <QtWidgets/QWidget>
+
+QT_BEGIN_NAMESPACE
+
+class Ui_MainWindow
+{
+public:
+    QWidget *centralWidget;
+    QVBoxLayout *verticalLayout_2;
+    QVBoxLayout *verticalLayout;
+    QHBoxLayout *horizontalLayout_10;
+    QLabel *label_11;
+    QLineEdit *casparcg_host_box;
+    QLineEdit *casparcg_port_box;
+    QPushButton *casparcg_reconnect_btn;
+    QLabel *casparcg_connected_label;
+    QHBoxLayout *horizontalLayout_3;
+    QGridLayout *gridLayout;
+    QHBoxLayout *horizontalLayout_4;
+    QPushButton *goal_2_btn;
+    QPushButton *ungoal_2_btn;
+    QLabel *label_6;
+    QSpinBox *score_2_box;
+    QPushButton *set_score_btn;
+    QLabel *label_7;
+    QLineEdit *initials_1_edit;
+    QHBoxLayout *horizontalLayout_5;
+    QPushButton *goal_1_btn;
+    QPushButton *ungoal_1_btn;
+    QLineEdit *color_1_edit;
+    QSpinBox *score_1_box;
+    QLabel *label_4;
+    QLabel *label_3;
+    QLineEdit *initials_2_edit;
+    QLabel *label_5;
+    QPushButton *set_initials_btn;
+    QLineEdit *color_2_edit;
+    QPushButton *set_color_btn;
+    QPushButton *set_all_scorebug_btn;
+    QFrame *line_3;
+    QHBoxLayout *horizontalLayout_2;
+    QLabel *label;
+    QSpinBox *clock_min_box;
+    QLabel *label_2;
+    QSpinBox *clock_sec_box;
+    QPushButton *set_clock_btn;
+    QHBoxLayout *horizontalLayout;
+    QPushButton *start_and_show_clock_btn;
+    QPushButton *stop_clock_btn;
+    QPushButton *show_clock_btn;
+    QPushButton *hide_clock_btn;
+    QFrame *line_2;
+    QHBoxLayout *horizontalLayout_6;
+    QLabel *label_8;
+    QLineEdit *comment_edit;
+    QPushButton *set_comment_btn;
+    QPushButton *set_and_show_comment_btn;
+    QPushButton *hide_comment_btn;
+    QHBoxLayout *horizontalLayout_11;
+    QLabel *label_12;
+    QLineEdit *autocomment_edit;
+    QPushButton *set_and_show_autocomment_btn;
+    QFrame *line;
+    QHBoxLayout *horizontalLayout_7;
+    QLabel *label_9;
+    QLineEdit *lowerthird_heading_edit;
+    QHBoxLayout *horizontalLayout_8;
+    QLabel *label_10;
+    QLineEdit *lowerthird_subheading_edit;
+    QHBoxLayout *horizontalLayout_9;
+    QPushButton *show_lower_third_btn;
+    QPushButton *hide_lower_third_btn;
+    QStatusBar *statusBar;
+
+    void setupUi(QMainWindow *MainWindow)
+    {
+        if (MainWindow->objectName().isEmpty())
+            MainWindow->setObjectName(QStringLiteral("MainWindow"));
+        MainWindow->resize(720, 552);
+        centralWidget = new QWidget(MainWindow);
+        centralWidget->setObjectName(QStringLiteral("centralWidget"));
+        verticalLayout_2 = new QVBoxLayout(centralWidget);
+        verticalLayout_2->setSpacing(6);
+        verticalLayout_2->setContentsMargins(11, 11, 11, 11);
+        verticalLayout_2->setObjectName(QStringLiteral("verticalLayout_2"));
+        verticalLayout = new QVBoxLayout();
+        verticalLayout->setSpacing(6);
+        verticalLayout->setObjectName(QStringLiteral("verticalLayout"));
+        verticalLayout->setContentsMargins(6, 6, 6, 6);
+        horizontalLayout_10 = new QHBoxLayout();
+        horizontalLayout_10->setSpacing(6);
+        horizontalLayout_10->setObjectName(QStringLiteral("horizontalLayout_10"));
+        label_11 = new QLabel(centralWidget);
+        label_11->setObjectName(QStringLiteral("label_11"));
+
+        horizontalLayout_10->addWidget(label_11);
+
+        casparcg_host_box = new QLineEdit(centralWidget);
+        casparcg_host_box->setObjectName(QStringLiteral("casparcg_host_box"));
+
+        horizontalLayout_10->addWidget(casparcg_host_box);
+
+        casparcg_port_box = new QLineEdit(centralWidget);
+        casparcg_port_box->setObjectName(QStringLiteral("casparcg_port_box"));
+        casparcg_port_box->setMaximumSize(QSize(50, 16777215));
+
+        horizontalLayout_10->addWidget(casparcg_port_box);
+
+        casparcg_reconnect_btn = new QPushButton(centralWidget);
+        casparcg_reconnect_btn->setObjectName(QStringLiteral("casparcg_reconnect_btn"));
+
+        horizontalLayout_10->addWidget(casparcg_reconnect_btn);
+
+        casparcg_connected_label = new QLabel(centralWidget);
+        casparcg_connected_label->setObjectName(QStringLiteral("casparcg_connected_label"));
+        casparcg_connected_label->setMinimumSize(QSize(100, 0));
+        casparcg_connected_label->setAlignment(Qt::AlignCenter);
+
+        horizontalLayout_10->addWidget(casparcg_connected_label);
+
+
+        verticalLayout->addLayout(horizontalLayout_10);
+
+        horizontalLayout_3 = new QHBoxLayout();
+        horizontalLayout_3->setSpacing(6);
+        horizontalLayout_3->setObjectName(QStringLiteral("horizontalLayout_3"));
+        gridLayout = new QGridLayout();
+        gridLayout->setSpacing(6);
+        gridLayout->setObjectName(QStringLiteral("gridLayout"));
+        horizontalLayout_4 = new QHBoxLayout();
+        horizontalLayout_4->setSpacing(6);
+        horizontalLayout_4->setObjectName(QStringLiteral("horizontalLayout_4"));
+        goal_2_btn = new QPushButton(centralWidget);
+        goal_2_btn->setObjectName(QStringLiteral("goal_2_btn"));
+
+        horizontalLayout_4->addWidget(goal_2_btn);
+
+        ungoal_2_btn = new QPushButton(centralWidget);
+        ungoal_2_btn->setObjectName(QStringLiteral("ungoal_2_btn"));
+
+        horizontalLayout_4->addWidget(ungoal_2_btn);
+
+
+        gridLayout->addLayout(horizontalLayout_4, 4, 2, 1, 1);
+
+        label_6 = new QLabel(centralWidget);
+        label_6->setObjectName(QStringLiteral("label_6"));
+
+        gridLayout->addWidget(label_6, 3, 0, 1, 1);
+
+        score_2_box = new QSpinBox(centralWidget);
+        score_2_box->setObjectName(QStringLiteral("score_2_box"));
+
+        gridLayout->addWidget(score_2_box, 3, 2, 1, 1);
+
+        set_score_btn = new QPushButton(centralWidget);
+        set_score_btn->setObjectName(QStringLiteral("set_score_btn"));
+
+        gridLayout->addWidget(set_score_btn, 3, 3, 1, 1);
+
+        label_7 = new QLabel(centralWidget);
+        label_7->setObjectName(QStringLiteral("label_7"));
+
+        gridLayout->addWidget(label_7, 2, 0, 1, 1);
+
+        initials_1_edit = new QLineEdit(centralWidget);
+        initials_1_edit->setObjectName(QStringLiteral("initials_1_edit"));
+
+        gridLayout->addWidget(initials_1_edit, 1, 1, 1, 1);
+
+        horizontalLayout_5 = new QHBoxLayout();
+        horizontalLayout_5->setSpacing(6);
+        horizontalLayout_5->setObjectName(QStringLiteral("horizontalLayout_5"));
+        goal_1_btn = new QPushButton(centralWidget);
+        goal_1_btn->setObjectName(QStringLiteral("goal_1_btn"));
+
+        horizontalLayout_5->addWidget(goal_1_btn);
+
+        ungoal_1_btn = new QPushButton(centralWidget);
+        ungoal_1_btn->setObjectName(QStringLiteral("ungoal_1_btn"));
+
+        horizontalLayout_5->addWidget(ungoal_1_btn);
+
+
+        gridLayout->addLayout(horizontalLayout_5, 4, 1, 1, 1);
+
+        color_1_edit = new QLineEdit(centralWidget);
+        color_1_edit->setObjectName(QStringLiteral("color_1_edit"));
+
+        gridLayout->addWidget(color_1_edit, 2, 1, 1, 1);
+
+        score_1_box = new QSpinBox(centralWidget);
+        score_1_box->setObjectName(QStringLiteral("score_1_box"));
+
+        gridLayout->addWidget(score_1_box, 3, 1, 1, 1);
+
+        label_4 = new QLabel(centralWidget);
+        label_4->setObjectName(QStringLiteral("label_4"));
+
+        gridLayout->addWidget(label_4, 0, 2, 1, 1);
+
+        label_3 = new QLabel(centralWidget);
+        label_3->setObjectName(QStringLiteral("label_3"));
+
+        gridLayout->addWidget(label_3, 0, 1, 1, 1);
+
+        initials_2_edit = new QLineEdit(centralWidget);
+        initials_2_edit->setObjectName(QStringLiteral("initials_2_edit"));
+
+        gridLayout->addWidget(initials_2_edit, 1, 2, 1, 1);
+
+        label_5 = new QLabel(centralWidget);
+        label_5->setObjectName(QStringLiteral("label_5"));
+
+        gridLayout->addWidget(label_5, 1, 0, 1, 1);
+
+        set_initials_btn = new QPushButton(centralWidget);
+        set_initials_btn->setObjectName(QStringLiteral("set_initials_btn"));
+
+        gridLayout->addWidget(set_initials_btn, 1, 3, 1, 1);
+
+        color_2_edit = new QLineEdit(centralWidget);
+        color_2_edit->setObjectName(QStringLiteral("color_2_edit"));
+
+        gridLayout->addWidget(color_2_edit, 2, 2, 1, 1);
+
+        set_color_btn = new QPushButton(centralWidget);
+        set_color_btn->setObjectName(QStringLiteral("set_color_btn"));
+
+        gridLayout->addWidget(set_color_btn, 2, 3, 1, 1);
+
+        set_all_scorebug_btn = new QPushButton(centralWidget);
+        set_all_scorebug_btn->setObjectName(QStringLiteral("set_all_scorebug_btn"));
+
+        gridLayout->addWidget(set_all_scorebug_btn, 4, 3, 1, 1);
+
+
+        horizontalLayout_3->addLayout(gridLayout);
+
+
+        verticalLayout->addLayout(horizontalLayout_3);
+
+        line_3 = new QFrame(centralWidget);
+        line_3->setObjectName(QStringLiteral("line_3"));
+        line_3->setFrameShape(QFrame::HLine);
+        line_3->setFrameShadow(QFrame::Sunken);
+
+        verticalLayout->addWidget(line_3);
+
+        horizontalLayout_2 = new QHBoxLayout();
+        horizontalLayout_2->setSpacing(6);
+        horizontalLayout_2->setObjectName(QStringLiteral("horizontalLayout_2"));
+        label = new QLabel(centralWidget);
+        label->setObjectName(QStringLiteral("label"));
+
+        horizontalLayout_2->addWidget(label);
+
+        clock_min_box = new QSpinBox(centralWidget);
+        clock_min_box->setObjectName(QStringLiteral("clock_min_box"));
+        QSizePolicy sizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
+        sizePolicy.setHorizontalStretch(0);
+        sizePolicy.setVerticalStretch(0);
+        sizePolicy.setHeightForWidth(clock_min_box->sizePolicy().hasHeightForWidth());
+        clock_min_box->setSizePolicy(sizePolicy);
+        clock_min_box->setValue(25);
+
+        horizontalLayout_2->addWidget(clock_min_box);
+
+        label_2 = new QLabel(centralWidget);
+        label_2->setObjectName(QStringLiteral("label_2"));
+        QSizePolicy sizePolicy1(QSizePolicy::Fixed, QSizePolicy::Preferred);
+        sizePolicy1.setHorizontalStretch(0);
+        sizePolicy1.setVerticalStretch(0);
+        sizePolicy1.setHeightForWidth(label_2->sizePolicy().hasHeightForWidth());
+        label_2->setSizePolicy(sizePolicy1);
+
+        horizontalLayout_2->addWidget(label_2);
+
+        clock_sec_box = new QSpinBox(centralWidget);
+        clock_sec_box->setObjectName(QStringLiteral("clock_sec_box"));
+        sizePolicy.setHeightForWidth(clock_sec_box->sizePolicy().hasHeightForWidth());
+        clock_sec_box->setSizePolicy(sizePolicy);
+        clock_sec_box->setMaximum(59);
+
+        horizontalLayout_2->addWidget(clock_sec_box);
+
+        set_clock_btn = new QPushButton(centralWidget);
+        set_clock_btn->setObjectName(QStringLiteral("set_clock_btn"));
+
+        horizontalLayout_2->addWidget(set_clock_btn);
+
+
+        verticalLayout->addLayout(horizontalLayout_2);
+
+        horizontalLayout = new QHBoxLayout();
+        horizontalLayout->setSpacing(6);
+        horizontalLayout->setObjectName(QStringLiteral("horizontalLayout"));
+        start_and_show_clock_btn = new QPushButton(centralWidget);
+        start_and_show_clock_btn->setObjectName(QStringLiteral("start_and_show_clock_btn"));
+
+        horizontalLayout->addWidget(start_and_show_clock_btn);
+
+        stop_clock_btn = new QPushButton(centralWidget);
+        stop_clock_btn->setObjectName(QStringLiteral("stop_clock_btn"));
+
+        horizontalLayout->addWidget(stop_clock_btn);
+
+        show_clock_btn = new QPushButton(centralWidget);
+        show_clock_btn->setObjectName(QStringLiteral("show_clock_btn"));
+
+        horizontalLayout->addWidget(show_clock_btn);
+
+        hide_clock_btn = new QPushButton(centralWidget);
+        hide_clock_btn->setObjectName(QStringLiteral("hide_clock_btn"));
+
+        horizontalLayout->addWidget(hide_clock_btn);
+
+
+        verticalLayout->addLayout(horizontalLayout);
+
+        line_2 = new QFrame(centralWidget);
+        line_2->setObjectName(QStringLiteral("line_2"));
+        line_2->setFrameShape(QFrame::HLine);
+        line_2->setFrameShadow(QFrame::Sunken);
+
+        verticalLayout->addWidget(line_2);
+
+        horizontalLayout_6 = new QHBoxLayout();
+        horizontalLayout_6->setSpacing(6);
+        horizontalLayout_6->setObjectName(QStringLiteral("horizontalLayout_6"));
+        label_8 = new QLabel(centralWidget);
+        label_8->setObjectName(QStringLiteral("label_8"));
+
+        horizontalLayout_6->addWidget(label_8);
+
+        comment_edit = new QLineEdit(centralWidget);
+        comment_edit->setObjectName(QStringLiteral("comment_edit"));
+
+        horizontalLayout_6->addWidget(comment_edit);
+
+        set_comment_btn = new QPushButton(centralWidget);
+        set_comment_btn->setObjectName(QStringLiteral("set_comment_btn"));
+
+        horizontalLayout_6->addWidget(set_comment_btn);
+
+        set_and_show_comment_btn = new QPushButton(centralWidget);
+        set_and_show_comment_btn->setObjectName(QStringLiteral("set_and_show_comment_btn"));
+
+        horizontalLayout_6->addWidget(set_and_show_comment_btn);
+
+        hide_comment_btn = new QPushButton(centralWidget);
+        hide_comment_btn->setObjectName(QStringLiteral("hide_comment_btn"));
+
+        horizontalLayout_6->addWidget(hide_comment_btn);
+
+
+        verticalLayout->addLayout(horizontalLayout_6);
+
+        horizontalLayout_11 = new QHBoxLayout();
+        horizontalLayout_11->setSpacing(6);
+        horizontalLayout_11->setObjectName(QStringLiteral("horizontalLayout_11"));
+        label_12 = new QLabel(centralWidget);
+        label_12->setObjectName(QStringLiteral("label_12"));
+
+        horizontalLayout_11->addWidget(label_12);
+
+        autocomment_edit = new QLineEdit(centralWidget);
+        autocomment_edit->setObjectName(QStringLiteral("autocomment_edit"));
+        autocomment_edit->setEnabled(true);
+
+        horizontalLayout_11->addWidget(autocomment_edit);
+
+        set_and_show_autocomment_btn = new QPushButton(centralWidget);
+        set_and_show_autocomment_btn->setObjectName(QStringLiteral("set_and_show_autocomment_btn"));
+
+        horizontalLayout_11->addWidget(set_and_show_autocomment_btn);
+
+
+        verticalLayout->addLayout(horizontalLayout_11);
+
+        line = new QFrame(centralWidget);
+        line->setObjectName(QStringLiteral("line"));
+        line->setFrameShape(QFrame::HLine);
+        line->setFrameShadow(QFrame::Sunken);
+
+        verticalLayout->addWidget(line);
+
+        horizontalLayout_7 = new QHBoxLayout();
+        horizontalLayout_7->setSpacing(6);
+        horizontalLayout_7->setObjectName(QStringLiteral("horizontalLayout_7"));
+        label_9 = new QLabel(centralWidget);
+        label_9->setObjectName(QStringLiteral("label_9"));
+
+        horizontalLayout_7->addWidget(label_9);
+
+        lowerthird_heading_edit = new QLineEdit(centralWidget);
+        lowerthird_heading_edit->setObjectName(QStringLiteral("lowerthird_heading_edit"));
+
+        horizontalLayout_7->addWidget(lowerthird_heading_edit);
+
+
+        verticalLayout->addLayout(horizontalLayout_7);
+
+        horizontalLayout_8 = new QHBoxLayout();
+        horizontalLayout_8->setSpacing(6);
+        horizontalLayout_8->setObjectName(QStringLiteral("horizontalLayout_8"));
+        label_10 = new QLabel(centralWidget);
+        label_10->setObjectName(QStringLiteral("label_10"));
+
+        horizontalLayout_8->addWidget(label_10);
+
+        lowerthird_subheading_edit = new QLineEdit(centralWidget);
+        lowerthird_subheading_edit->setObjectName(QStringLiteral("lowerthird_subheading_edit"));
+
+        horizontalLayout_8->addWidget(lowerthird_subheading_edit);
+
+
+        verticalLayout->addLayout(horizontalLayout_8);
+
+        horizontalLayout_9 = new QHBoxLayout();
+        horizontalLayout_9->setSpacing(6);
+        horizontalLayout_9->setObjectName(QStringLiteral("horizontalLayout_9"));
+        show_lower_third_btn = new QPushButton(centralWidget);
+        show_lower_third_btn->setObjectName(QStringLiteral("show_lower_third_btn"));
+
+        horizontalLayout_9->addWidget(show_lower_third_btn);
+
+        hide_lower_third_btn = new QPushButton(centralWidget);
+        hide_lower_third_btn->setObjectName(QStringLiteral("hide_lower_third_btn"));
+
+        horizontalLayout_9->addWidget(hide_lower_third_btn);
+
+
+        verticalLayout->addLayout(horizontalLayout_9);
+
+
+        verticalLayout_2->addLayout(verticalLayout);
+
+        MainWindow->setCentralWidget(centralWidget);
+        statusBar = new QStatusBar(MainWindow);
+        statusBar->setObjectName(QStringLiteral("statusBar"));
+        MainWindow->setStatusBar(statusBar);
+
+        retranslateUi(MainWindow);
+
+        QMetaObject::connectSlotsByName(MainWindow);
+    } // setupUi
+
+    void retranslateUi(QMainWindow *MainWindow)
+    {
+        MainWindow->setWindowTitle(QApplication::translate("MainWindow", "MainWindow", Q_NULLPTR));
+        label_11->setText(QApplication::translate("MainWindow", "CasparCG server:", Q_NULLPTR));
+        casparcg_host_box->setText(QApplication::translate("MainWindow", "localhost", Q_NULLPTR));
+        casparcg_port_box->setText(QApplication::translate("MainWindow", "5250", Q_NULLPTR));
+        casparcg_reconnect_btn->setText(QApplication::translate("MainWindow", "Reconnect", Q_NULLPTR));
+        casparcg_connected_label->setText(QApplication::translate("MainWindow", "Not connected", Q_NULLPTR));
+        goal_2_btn->setText(QApplication::translate("MainWindow", "+1 point", Q_NULLPTR));
+        ungoal_2_btn->setText(QApplication::translate("MainWindow", "-1 point", Q_NULLPTR));
+        label_6->setText(QApplication::translate("MainWindow", "Score", Q_NULLPTR));
+        set_score_btn->setText(QApplication::translate("MainWindow", "Set", Q_NULLPTR));
+        label_7->setText(QApplication::translate("MainWindow", "CSS color", Q_NULLPTR));
+        initials_1_edit->setText(QApplication::translate("MainWindow", "PCL", Q_NULLPTR));
+        goal_1_btn->setText(QApplication::translate("MainWindow", "+1 point", Q_NULLPTR));
+        ungoal_1_btn->setText(QApplication::translate("MainWindow", "-1 point", Q_NULLPTR));
+        color_1_edit->setText(QApplication::translate("MainWindow", "red", Q_NULLPTR));
+        label_4->setText(QApplication::translate("MainWindow", "Team 2", Q_NULLPTR));
+        label_3->setText(QApplication::translate("MainWindow", "Team 1", Q_NULLPTR));
+        initials_2_edit->setText(QApplication::translate("MainWindow", "TFK", Q_NULLPTR));
+        label_5->setText(QApplication::translate("MainWindow", "Initials", Q_NULLPTR));
+        set_initials_btn->setText(QApplication::translate("MainWindow", "Set", Q_NULLPTR));
+        color_2_edit->setText(QApplication::translate("MainWindow", "yellow", Q_NULLPTR));
+        set_color_btn->setText(QApplication::translate("MainWindow", "Set", Q_NULLPTR));
+        set_all_scorebug_btn->setText(QApplication::translate("MainWindow", "Set all", Q_NULLPTR));
+        label->setText(QApplication::translate("MainWindow", "Set clock to:", Q_NULLPTR));
+        label_2->setText(QApplication::translate("MainWindow", ":", Q_NULLPTR));
+        set_clock_btn->setText(QApplication::translate("MainWindow", "Set", Q_NULLPTR));
+        start_and_show_clock_btn->setText(QApplication::translate("MainWindow", "Start (and show) clock", Q_NULLPTR));
+        stop_clock_btn->setText(QApplication::translate("MainWindow", "Stop clock", Q_NULLPTR));
+        show_clock_btn->setText(QApplication::translate("MainWindow", "Show clock", Q_NULLPTR));
+        hide_clock_btn->setText(QApplication::translate("MainWindow", "Hide clock", Q_NULLPTR));
+        label_8->setText(QApplication::translate("MainWindow", "Comment:", Q_NULLPTR));
+        comment_edit->setText(QApplication::translate("MainWindow", "Pagacap: First to 9 points", Q_NULLPTR));
+        set_comment_btn->setText(QApplication::translate("MainWindow", "Set", Q_NULLPTR));
+        set_and_show_comment_btn->setText(QApplication::translate("MainWindow", "Set+show", Q_NULLPTR));
+        hide_comment_btn->setText(QApplication::translate("MainWindow", "Hide", Q_NULLPTR));
+        label_12->setText(QApplication::translate("MainWindow", "Suggested autocomment if game ends right now:", Q_NULLPTR));
+        autocomment_edit->setText(QString());
+        set_and_show_autocomment_btn->setText(QApplication::translate("MainWindow", "Set+show", Q_NULLPTR));
+        label_9->setText(QApplication::translate("MainWindow", "Lower third heading (HTML allowed):", Q_NULLPTR));
+        label_10->setText(QApplication::translate("MainWindow", "Lower third subheading (HTML allowed):", Q_NULLPTR));
+        show_lower_third_btn->setText(QApplication::translate("MainWindow", "Set + show lower third", Q_NULLPTR));
+        hide_lower_third_btn->setText(QApplication::translate("MainWindow", "Hide lower third", Q_NULLPTR));
+    } // retranslateUi
+
+};
+
+namespace Ui {
+    class MainWindow: public Ui_MainWindow {};
+} // namespace Ui
+
+QT_END_NAMESPACE
+
+#endif // UI_MAINWINDOW_H
diff --git a/lowerthird-bg.png b/lowerthird-bg.png
new file mode 100644 (file)
index 0000000..ea954d9
Binary files /dev/null and b/lowerthird-bg.png differ
diff --git a/lowerthird-bg2.png b/lowerthird-bg2.png
new file mode 100644 (file)
index 0000000..4b80dff
Binary files /dev/null and b/lowerthird-bg2.png differ
diff --git a/score.html b/score.html
new file mode 100644 (file)
index 0000000..0764d5d
--- /dev/null
@@ -0,0 +1,596 @@
+<!DOCTYPE html>
+<html>
+ <head>
+   <meta http-equiv="content-type" value="text/html; charset=utf-8" />
+   <meta charset="utf-8" />
+   <link href="https://fonts.googleapis.com/css?family=Lato" rel="stylesheet">
+   <style>
+* {
+  -webkit-box-sizing: border-box;
+  Box-Sizing: border-box;
+  -webkit-backface-visibility: hidden;
+  -webkit-transition: translate3d(0,0,0);
+}
+html, body {
+  width: 1280px;
+  height: 720px;
+  margin: 0;
+  padding: 0;
+  background: transparent;
+  overflow: hidden;
+  -webkit-font-smoothing: antialiased !important;
+}
+body {
+  font-family: 'Lato', sans-serif;
+  color: white;
+}
+
+/* Score */
+.scorebug {
+  position: fixed;
+  font-size: 25px;
+  left: 10px;
+  top: 10px;
+  border-collapse: collapse;
+  white-space: nowrap;
+  border: 3px solid black;
+  z-index: 1;
+}
+.scorebug td {
+  border: 3px solid #008;
+}
+.team1color, .team2color {
+  width: 15px;
+  margin: 5px;
+}
+.team1color {
+  background: red;
+}
+.team2color {
+  background: green;
+}
+.team1, .team2 {
+  font-weight: bold;
+  width: 100px;
+  text-align: center;
+  height: 35px;
+  background: #00a;
+}
+.score {
+  text-align: center;
+  background: #00c;
+  width: 110px;
+  height: 35px;
+}
+
+/* Clock, to the right of score */
+.clockbug {
+  position: fixed;
+  font-size: 25px;
+  left: 360px;
+  top: 10px;
+  border-collapse: collapse;
+  white-space: nowrap;
+  border: 3px solid black;
+}
+.clock {
+  border: 3px solid #008;
+  background: #00a;
+  text-align: center;
+  height: 35px;
+  width: 90px;
+}
+.clockbug-hidden {
+  -webkit-transform:translateX(-200%);
+}
+.clockbug-animate-in {
+  -webkit-animation: from-left 1s ease;
+}
+.clockbug-animate-out {
+  -webkit-animation: to-left 1s ease;
+  -webkit-transform:translateX(-200%);
+}
+
+/* Comment, below score */
+.commentbug {
+  position: fixed;
+  font-size: 20px;
+  left: 10px;
+  top: 52px;
+  border-collapse: collapse;
+  white-space: nowrap;
+  border: 3px solid black;
+}
+.comment {
+  border: 3px solid #008;
+  background: #00a;
+  text-align: center;
+  height: 30px;
+  width: 340px;
+}
+
+.commentbug-hidden {
+  -webkit-transform:translateY(-40px);
+}
+.commentbug-animate-in {
+  -webkit-animation: from-top 0.5s ease;
+}
+.commentbug-animate-out {
+  -webkit-animation: to-top 0.5s ease;
+  -webkit-transform:translateY(-40px);
+}
+
+/* Lower third */
+.lowerthird-headline {
+  position: fixed;
+  font-size: 40px;
+  left: 10px;
+  top: 520px;
+  border-collapse: collapse;
+  white-space: nowrap;
+  border: 2px solid #ccc;
+  height: 118px;
+  font-weight: bold;
+  width: 1050px;
+  /*background: linear-gradient(to right, #ccc, #fff 15px); */
+  background-image: url(lowerthird-bg.png);
+  color: black;
+  /* padding-top: 20px; */
+}
+.lowerthird-headline-hidden {
+  width: 0px;
+  border: 0px;
+}
+.lowerthird-headline-animate-in {
+  -webkit-animation: scale-out 1.0s;
+}
+.lowerthird-headline-animate-out {
+  -webkit-animation: scale-in 1.0s;
+}
+.lowerthird-headline-content {
+  padding-left: 20px;
+  position: absolute;
+}
+.lowerthird-headline-content-hidden {
+  clip: rect(0px,0px,200px,0px);
+}
+.lowerthird-headline-content-animate-in {
+  -webkit-animation: wipe-out 2.0s ease;
+}
+.lowerthird-headline-content-animate-out {
+  -webkit-animation: wipe-in 2.0s ease;
+  clip: rect(0px,0px,200px,0px);
+}
+.lowerthird-subheading {
+  position: fixed;
+  font-size: 24px;
+  left: 40px;
+  top: 637px;
+  height: 40px;
+  width: 500px;
+  border-collapse: collapse;
+  white-space: nowrap;
+  border: 1px solid black;
+  /*display: flex;
+  align-items: center; */
+  /*background: linear-gradient(to right, #44c, #33a 15px); */
+  background-image: url(lowerthird-bg2.png);
+}
+.lowerthird-subheading-hidden {
+  clip: rect(0px,0px,200px,0px);
+}
+.lowerthird-subheading-animate-in {
+  -webkit-animation: scale-out-small 1.6s ease;
+}
+.lowerthird-subheading-animate-out {
+  -webkit-animation: scale-in-small 1.6s ease;
+  width: 0px;
+  border-left: 0px;
+}
+.lowerthird-subheading-content {
+  position: absolute;
+  padding-left: 20px;
+}
+.lowerthird-subheading-content-hidden {
+  width: 0px;
+  border: 0px;
+}
+.lowerthird-subheading-content-animate-in {
+  -webkit-animation: wipe-out 2.2s ease;
+}
+.lowerthird-subheading-content-animate-out {
+  -webkit-animation: wipe-in 2.2s ease;
+  clip: rect(0px,0px,200px,0px);
+}
+
+/* these are hidden when actually run in casparcg */
+#area {
+  position: fixed;
+  left: 0px;
+  top: 0px;
+  width: 1280px;
+  height: 720px;
+  background: cyan;
+}
+#manualcontrols {
+  z-index: 3;
+  position: fixed;
+  top: 250px;
+}
+
+/* Animations */
+@-webkit-keyframes from-left {
+  0% {
+    -webkit-transform:translateX(-200%);
+  }
+  100% {
+    -webkit-transform:translateX(0);
+  }
+}
+@-webkit-keyframes to-left {
+  0% {
+    -webkit-transform:translateX(0);
+  }
+  100% {
+    -webkit-transform:translateX(-200%);
+  }
+}
+@-webkit-keyframes from-top {
+  0% {
+    -webkit-transform:translateY(-40px);
+  }
+  100% {
+    -webkit-transform:translateY(0);
+  }
+}
+@-webkit-keyframes to-top {
+  0% {
+    -webkit-transform:translateY(0);
+  }
+  100% {
+    -webkit-transform:translateY(-40px);
+  }
+}
+
+@-webkit-keyframes scale-out {
+  0% {
+    width: 0px;
+  }
+  100% {
+    width: 1050px;
+  }
+}
+@-webkit-keyframes scale-in {
+  0% {
+    width: 1050px;
+    border: 2px solid #ccc;
+  }
+  100% {
+    width: 0px;
+    border: 2px solid #ccc;
+  }
+}
+@-webkit-keyframes scale-out-small {
+  0% {
+    width: 0px;
+    border: 0px;
+  }
+  19.99% {
+    border: 0px;
+  }
+  20% {
+    width: 0px;
+    border: 1px solid black;
+  }
+  100% {
+    width: 500px;
+  }
+}
+@-webkit-keyframes scale-in-small {
+  0% {
+    width: 500px;
+    border: 1px solid black;
+  }
+  80% {
+    width: 0px;
+    border: 1px solid black;
+  }
+  80.01% {
+    border: 0px;
+  }
+  100% {
+    width: 0px;
+    border: 0px;
+  }
+}
+@-webkit-keyframes wipe-out {
+  0% {
+    clip: rect(0px,0px,200px,0px);
+  }
+  20% {
+    clip: rect(0px,0px,200px,0px);
+  }
+  100% {
+    clip: rect(0px,1050px,200px,0px);
+  }
+}
+@-webkit-keyframes wipe-in {
+  0% {
+    clip: rect(0px,1050px,200px,0px);
+  }
+  20% {  /* Not symmetrical! */
+    clip: rect(0px,0px,200px,0px);
+  }
+  100% {
+    clip: rect(0px,0px,200px,0px);
+  }
+}
+   </style>
+  </head>
+  <body>
+    <div id="area"></div>
+    <table class="scorebug">
+      <tr>
+        <td class="team1color" id="team1color"></td>
+        <td class="team1" id="team1">PCL</td>
+        <td class="score" id="score">17&nbsp;–&nbsp;11</td>
+        <td class="team2" id="team2">NMBUI</td>
+        <td class="team2color" id="team2color"></td>
+      </tr>
+    </table>
+    <table class="clockbug clockbug-hidden" id="clockbug">
+      <tr>
+        <td class="clock" id="clock">25:00</td>
+      </tr>
+    </table>
+    <table class="commentbug commentbug-hidden" id="commentbug">
+      <tr>
+        <td class="comment" id="comment">Pagacap: First to 9 points</td>
+      </tr>
+    </table>
+    <div class="lowerthird-headline lowerthird-headline-hidden" id="lowerthird-headline"><div class="lowerthird-headline-content lowerthird-headline-content-hidden" id="lowerthird-headline-content">
+      John Doe<br>Ola Nordmann
+    </div></div>
+    <div class="lowerthird-subheading lowerthird-subheading-hidden" id="lowerthird-subheading"><div class="lowerthird-subheading-content lowerthird-subheading-content-hidden" id="lowerthird-subheading-content">
+      Commentators, Trøndisk 2016
+    </div></div>
+   <div id="manualcontrols">
+    <p>
+      <a href="javascript:startclock()">start clock</a>
+      <a href="javascript:stopclock()">stop clock</a>
+      <a href="javascript:setclock(25*60)">reset clock</a>
+      <a href="javascript:showclock()">show clock</a>
+      <a href="javascript:hideclock()">hide clock</a>
+    </p>
+    <p>
+      <a href="javascript:goalA()">goal team A</a>
+      <a href="javascript:goalB()">goal team B</a>
+      <a href="javascript:ungoalA()">ungoal team A</a>
+      <a href="javascript:ungoalB()">ungoal team B</a>
+      <a href="javascript:resetscore()">reset score</a>
+    </p>
+    <p>
+      <a href="javascript:showcomment()">show comment</a>
+      <a href="javascript:hidecomment()">hide comment</a>
+    </p>
+    <p>
+      <a href="javascript:showlowerthird()">show lower third</a>
+      <a href="javascript:hidelowerthird()">hide lower third</a>
+    </p>
+   </div>
+
+<script>
+var clock_running = false;
+var clock_visible = false;
+var comment_visible = false;
+var lowerthird_visible = false;
+var clock_left = 25 * 60;
+var scoreA = 0;
+var scoreB = 0;
+var clock_origin;
+var state = {};
+
+function setteams()
+{
+       document.getElementById('team1').innerHTML = state['team1'];
+       document.getElementById('team2').innerHTML = state['team2'];
+}
+
+function setcolors()
+{
+       document.getElementById('team1color').style.backgroundColor = state['team1color'];
+       document.getElementById('team2color').style.backgroundColor = state['team2color'];
+}
+
+function setscore()
+{
+       scoreA = state['score1'];
+       scoreB = state['score2'];
+       update_score();
+}
+
+function startclock()
+{
+       if (!clock_running) {
+               clock_origin = Date.now();
+               clock_running = true;
+       }
+       showclock();
+}
+
+function stopclock()
+{
+       if (!clock_running) return;
+       clock_left = time_left();
+       clock_origin = Date.now();
+       clock_running = false;
+}
+
+function setclock(amount)
+{
+       clock_left = amount;
+       clock_origin = Date.now();
+       update_clock();
+}
+
+function setclockfromstate()
+{
+       var amount = parseInt(state['clock_min']) * 60 + parseInt(state['clock_sec']);
+       setclock(amount);
+}
+
+function showclock()
+{
+       if (clock_visible) return;
+       var clockbug = document.getElementById('clockbug');
+       clockbug.className = 'clockbug clockbug-animate-in';
+       clock_visible = true;
+}
+
+function hideclock()
+{
+       if (!clock_visible) return;
+       var clockbug = document.getElementById('clockbug');
+       clockbug.className = 'clockbug clockbug-animate-out';
+       clock_visible = false;
+}
+
+function setcomment()
+{
+       document.getElementById('comment').innerHTML = state['comment'];
+}
+
+function showcomment()
+{
+       if (comment_visible) return;
+       var commentbug = document.getElementById('commentbug');
+       commentbug.className = 'commentbug commentbug-animate-in';
+       comment_visible = true;
+}
+
+function hidecomment()
+{
+       if (!comment_visible) return;
+       var commentbug = document.getElementById('commentbug');
+       commentbug.className = 'commentbug commentbug-animate-out';
+       comment_visible = false;
+}
+
+function showlowerthird()
+{
+       if (lowerthird_visible) return;
+
+       // With no flexbox, this is how it has to be...
+       var f = document.getElementById('lowerthird-headline');
+       var g = document.getElementById('lowerthird-headline-content');
+       f.style.paddingTop = Math.round((f.clientHeight - g.clientHeight) / 2) + 'px';
+
+       f = document.getElementById('lowerthird-subheading');
+       g = document.getElementById('lowerthird-subheading-content');
+       f.style.paddingTop = Math.round((f.clientHeight - g.clientHeight) / 2) + 'px';
+
+       document.getElementById('lowerthird-headline').className = 'lowerthird-headline lowerthird-headline-animate-in';
+       document.getElementById('lowerthird-headline-content').className = 'lowerthird-headline-content lowerthird-headline-content-animate-in';
+       document.getElementById('lowerthird-subheading').className = 'lowerthird-subheading lowerthird-subheading-animate-in';
+       document.getElementById('lowerthird-subheading-content').className = 'lowerthird-subheading-content lowerthird-subheading-content-animate-in';
+       lowerthird_visible = true;
+}
+
+function setandshowlowerthird()
+{
+       document.getElementById('lowerthird-headline-content').innerHTML = state['text1'];
+       document.getElementById('lowerthird-subheading-content').innerHTML = state['text2'];
+       showlowerthird();
+}
+
+function hidelowerthird()
+{
+       if (!lowerthird_visible) return;
+       document.getElementById('lowerthird-headline').className = 'lowerthird-headline lowerthird-headline-hidden lowerthird-headline-animate-out';
+       document.getElementById('lowerthird-headline-content').className = 'lowerthird-headline-content lowerthird-headline-content-animate-out';
+       document.getElementById('lowerthird-subheading').className = 'lowerthird-subheading lowerthird-subheading-animate-out';
+       document.getElementById('lowerthird-subheading-content').className = 'lowerthird-subheading-content lowerthird-subheading-content-animate-out';
+       lowerthird_visible = false;
+}
+
+function time_left()
+{
+       var elapsed = (Date.now() - clock_origin) * 1e-3;
+       if (elapsed > clock_left) return 0;
+       return Math.ceil(clock_left - elapsed);
+}
+
+function update_clock()
+{
+       var left = time_left();
+       var min = Math.floor(left / 60);
+       var sec = left % 60;
+
+       if (sec < 10) sec = "0" + sec;
+       document.getElementById('clock').innerHTML = min + ":" + sec;
+}
+
+function goalA()
+{
+       ++scoreA;
+       update_score();
+}
+
+function goalB()
+{
+       ++scoreB;
+       update_score();
+}
+
+function ungoalA()
+{
+       if (scoreA > 0) --scoreA;
+       update_score();
+}
+
+function ungoalB()
+{
+       if (scoreB > 0) --scoreB;
+       update_score();
+}
+
+function resetscore()
+{
+       scoreA = scoreB = 0;
+       update_score();
+}
+
+function update_score()
+{
+       document.getElementById('score').innerHTML = scoreA + "&nbsp;–&nbsp;" + scoreB;
+}
+
+/* called by caspar only */
+function play()
+{
+       document.getElementById('manualcontrols').style.display = 'none';
+       document.getElementById('area').style.display = 'none';
+
+       // Old CEF workaround
+       document.getElementById('lowerthird-subheading').style.top = '638px';
+}
+
+function update(v)
+{
+       console.log('[[[' + v + ']]]');
+       var j = JSON.parse(v);
+       for(var key in j) state[key] = j[key];
+}
+
+setInterval(function() {
+       if (clock_running) {
+               update_clock();
+       }
+}, 100);
+update_score();
+
+//play();
+//startclock();
+</script>
+  </body>
+</html>