diff --git a/cpp/clivis/main.cpp b/cpp/clivis/main.cpp index 1ed8838..c18395d 100644 --- a/cpp/clivis/main.cpp +++ b/cpp/clivis/main.cpp @@ -126,19 +126,31 @@ static void showContents2(std::span data, std::size_t tick, std } int main(int argc, char** argv) { - if (argc != 2 || !argv[1]) { + if (argc < 2 || !argv[1]) { std::ostringstream buf; - buf << "Usage: " << argv[0] << " filename\n"; + buf << "Usage: " << argv[0] << " filename [timestamp]\n"; std::cerr << buf.str(); return 1; } if (std::string_view{argv[1]} == "--help") { std::ostringstream buf; - buf << "Usage: " << argv[0] << " filename\n"; + buf << "Usage: " << argv[0] << " filename [timestamp]\n"; std::cout << buf.str() << std::flush; return 0; } + std::int64_t timestamp = -1; + if (argc > 2) { + char* endptr = nullptr; + timestamp = strtoll(argv[2], &endptr, 10); + if (!endptr || *endptr) { + std::ostringstream buf; + buf << "Usage: " << argv[0] << " filename [timestamp]\n"; + std::cerr << buf.str(); + return 1; + } + } + try { auto contents = readEntireFile>(argv[1]); auto file = monoformat::parseFile(contents); @@ -159,11 +171,12 @@ int main(int argc, char** argv) { std::signal(SIGTERM, &handleCtrlC); std::size_t tick = 0; while (!g_ctrlCPressed.load(std::memory_order_relaxed)) { + std::int64_t currentTime = timestamp == -1 ? time(nullptr) : timestamp; clearScreen(); - //showContents(*file, tick, time(nullptr), customFonts); - showContents2(contents, tick, time(nullptr)); + //showContents(*file, tick, currentTime, customFonts); + showContents2(contents, tick, currentTime); std::ostringstream buf; - buf << "Animation Tick: " << tick << "\n"; + buf << "Animation Tick: " << tick << ", Timestamp = " << currentTime << "\n"; std::cout << buf.str() << std::flush; std::this_thread::sleep_for(std::chrono::milliseconds{40}); ++tick; diff --git a/cpp/src/monoformat_structured.cpp b/cpp/src/monoformat_structured.cpp index f2ccdea..7f2e02d 100644 --- a/cpp/src/monoformat_structured.cpp +++ b/cpp/src/monoformat_structured.cpp @@ -403,13 +403,13 @@ std::expected, ParseError> AnimationElement::p return std::unexpected(reserved_.error()); } std::size_t imageSize = ((*width) * (*height) + 8 - 1) / 8; - auto imageData = readBuffer(buffer, imageSize); + auto imageData = readBuffer(buffer, imageSize * (*numberOfFrames)); if (!imageData) { return std::unexpected(reserved_.error()); } - std::size_t rounded = (imageSize + 4 - 1) / 4 * 4; - if (imageSize < rounded) { - buffer = buffer.subspan(rounded - imageSize); + std::size_t rounded = (imageSize * (*numberOfFrames) + 4 - 1) / 4 * 4; + if (imageSize * (*numberOfFrames) < rounded) { + buffer = buffer.subspan(rounded - imageSize * (*numberOfFrames)); } auto result = std::make_unique(*x, *y, *width, *height, *numberOfFrames, *updateInterval); @@ -2883,7 +2883,7 @@ std::expected, ParseError> ExpiryDateSection: if (!type) { return std::unexpected(type.error()); } - if (*type != static_cast(SectionType::CustomFont)) { + if (*type != static_cast(SectionType::ExpiryDate)) { return std::unexpected(ParseError::InvalidValue); } auto size = readU24LE(buffer); diff --git a/php/monoformat_structured.php b/php/monoformat_structured.php index 804c332..cef9b83 100644 --- a/php/monoformat_structured.php +++ b/php/monoformat_structured.php @@ -103,7 +103,7 @@ class ImageElement extends Element { } public function setImage(array $image) { - $n = $m_width * $m_height; + $n = $this->m_width * $this->m_height; if (count($image) != $n) { $actual = count($image); throw new \ValueError("Cannot update the image of an ImageElement: the number of pixels supplied, $actual, is not the expected number, $n"); @@ -222,7 +222,7 @@ class AnimationElement extends Element { $max = count($this->m_frames) - 1; throw new \ValueError("Cannot retrieve a frame of an AnimationElement: the frame index $n is not in the range [0..$max]"); } - $nPixels = $m_width * $m_height; + $nPixels = $this->m_width * $this->m_height; if (count($image) != $nPixels) { $actual = count($image); throw new \ValueError("Cannot update a frame of an AnimationElement: the number of pixels supplied, $actual, is not the expected number, $nPixels"); @@ -251,7 +251,7 @@ class AnimationElement extends Element { } public function serializeBinary(int $formatVersion): string { - $result = pack("vvvvvvvvv", + $result = pack("vvvvvvvv", ElementType::Animation->value, $this->m_x, $this->m_y, @@ -280,7 +280,7 @@ class AnimationElement extends Element { public static function parseBinary(string &$data, int $formatVersion): AnimationElement { [$type, $x, $y, $width, $height, $nFrames, $updateInterval, $reserved] = array_values(unpack("v8", substr($data, 0, 16))); $type = ElementType::from($type); - if ($type != ElementType::AnimationElement) { + if ($type != ElementType::Animation) { throw new \ValueError("Could not unserialize an animation element: type mismatch"); } $n = $width * $height; @@ -352,7 +352,7 @@ class HScrollImageElement extends Element { } public function setImage(array $image) { - $n = $m_contentWidth * $m_height; + $n = $this->m_contentWidth * $this->m_height; if (count($image) != $n) { $actual = count($image); throw new \ValueError("Cannot update the image of an HScrollImageElement: the number of pixels supplied, $actual, is not the expected number, $n"); @@ -481,7 +481,7 @@ class VScrollImageElement extends Element { } public function setImage(array $image) { - $n = $m_width * $m_contentHeight; + $n = $this->m_width * $this->m_contentHeight; if (count($image) != $n) { $actual = count($image); throw new \ValueError("Cannot update the image of a VScrollImageElement: the number of pixels supplied, $actual, is not the expected number, $n"); @@ -950,7 +950,7 @@ class HScrollTextElement extends Element { } public function flags(): ScrollFlags { - return $this->m_textFlags; + return $this->m_flags; } public function scrollSpeed(): int { diff --git a/php/schedule.php b/php/schedule.php new file mode 100644 index 0000000..4789a61 --- /dev/null +++ b/php/schedule.php @@ -0,0 +1,178 @@ += count($talks)) { + return ""; + } + $talk = $talks[$id]; + if (array_key_exists($what, $talk)) { + return $talk[$what]; + } else { + return ""; + } + } else { + return ""; + } + }, $str); +} + +$roomMapping = [ +]; + +if (!isset($_GET["mac"]) or !array_key_exists($_GET["mac"], $roomMapping)) { + /* We didn't find this device in the mapping, so let's just + * send a default file to the user. + */ + Header("Content-Type: application/octet-stream"); + readfile(dirname(__FILE__) . "/noroom.bin"); + exit(0); +} + +$roomGUID = strtolower($roomMapping[$_GET["mac"]]); + +$pretalx = json_decode(file_get_contents("schedule.json"), true); + +$roomName = null; +foreach ($pretalx["schedule"]["conference"]["rooms"] as $room) { + $thisRoomGUID = strtolower($room["guid"]); + if ($thisRoomGUID === $roomGUID) { + $roomName = $room["name"]; + break; + } +} +if ($roomName === null) { + die("Room not found!"); +} + +$talks = []; +$now = new DateTimeImmutable("now"); + +foreach ($pretalx["schedule"]["conference"]["days"] as $day) { + if (!array_key_exists($roomName, $day["rooms"])) { + continue; + } + $roomTalks = $day["rooms"][$roomName]; + foreach ($roomTalks as $t) { + [$h, $m] = explode(":", $t["duration"]); + $duration = $h * 3600 + $m * 60; + $duration = DateInterval::createFromDateString("$duration sec"); + $speakers = []; + foreach ($t["persons"] as $person) { + array_push($speakers, $person["name"]); + } + $talkBegin = new DateTimeImmutable($t["date"]); + $talkEnd = $talkBegin->add($duration); + if ($talkEnd < $now) { + continue; + } + $talk = [ + "Time" => $talkBegin->format("H:i"), + "Duration" => $duration->format("H:i"), + "Persons" => implode(", ", $speakers), + "Title" => $t["title"], + "_start" => $talkBegin, + "_end" => $talkEnd, + ]; + array_push($talks, $talk); + } +} + +$template = file_get_contents(dirname(__FILE__) . "/template.bin"); +$template = monoformat\parseFile($template); + +if (count($template->sections) != 1 or $template->sections[0]->sectionType() != monoformat\SectionType::AlwaysDrawn) { + die("Invalid section in template file"); +} + +$elements = []; +for ($i = 0; $i < $template->sections[0]->elementCount(); ++$i) { + array_push($elements, $template->sections[0]->elementAt($i)); +} + +$end_template = file_get_contents(dirname(__FILE__) . "/endscreen.bin"); +$end_template = monoformat\parseFile($end_template); + +if (count($end_template->sections) != 1 or $end_template->sections[0]->sectionType() != monoformat\SectionType::AlwaysDrawn) { + die("Invalid section in end screen template file"); +} + +$end_elements = []; +for ($i = 0; $i < $end_template->sections[0]->elementCount(); ++$i) { + array_push($end_elements, $end_template->sections[0]->elementAt($i)); +} + +$n = 42; +$last = false; +if ($n > count($talks)) { + $n = count($talks); + $last = true; +} + +$lastEnd = 0; +$outputFile = new monoformat\File(); +if (!$last) { + $section = new monoformat\ExpiryDateSection(); + $section->setExpiryDate($talks[$n - 1]["_end"]->getTimestamp()); + array_push($outputFile->sections, $section); +} +for ($i = 0; $i < $n; ++$i) { + $subTalks = array_slice($talks, $i); + $end = $subTalks[0]["_end"]->getTimestamp(); + $section = new monoformat\TimeBasedDrawnSection(); + $section->setDrawOnFront(true); + $section->setDrawOnBack(true); + $section->setClearBeforeDrawing(true); + $section->setStartTimestamp($lastEnd); + $section->setEndTimestamp($end); + foreach ($elements as $element) { + if ($element->elementType() == monoformat\ElementType::ClippedText) { + $x = $element->x(); + $y = $element->y(); + $width = $element->width(); + $height = $element->height(); + $textFlags = $element->textFlags(); + $fontIndex = $element->fontIndex(); + $text = replaceTalkInfo($element->text(), $subTalks); + $element = new monoformat\ClippedTextElement($x, $y, $width, $height, $textFlags, $fontIndex, $text); + } else if ($element->elementType() == monoformat\ElementType::HScrollText) { + $x = $element->x(); + $y = $element->y(); + $width = $element->width(); + $height = $element->height(); + $textFlags = $element->textFlags(); + $flags = $element->flags(); + $scrollSpeed = $element->scrollSpeed(); + $fontIndex = $element->fontIndex(); + $text = replaceTalkInfo($element->text(), $subTalks) . " "; + $element = new monoformat\HScrollTextElement($x, $y, $width, $height, $textFlags, $flags, $scrollSpeed, $fontIndex, $text); + } + $section->appendElement($element); + } + array_push($outputFile->sections, $section); + $lastEnd = $end; +} +if ($last) { + $section = new monoformat\TimeBasedDrawnSection(); + $section->setDrawOnFront(true); + $section->setDrawOnBack(true); + $section->setClearBeforeDrawing(true); + $section->setStartTimestamp($lastEnd); + // Set this far enough in the future that it doesn't matter + $section->setEndTimestamp($lastEnd + 86400 * 300); + foreach ($end_elements as $element) { + $section->appendElement($element); + } + array_push($outputFile->sections, $section); +} + +Header("Content-Type: application/octet-stream"); +print(monoformat\serializeFile($outputFile)); + +?>