diff --git a/Dockerfile b/Dockerfile index 28ef297..2817a32 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,7 @@ FROM ubuntu:16.04 ENV TERM=xterm-256color -ENV OF_VERSION "0.9.8" +ENV OF_VERSION "0.10.1-patched" ENV OF_ROOT "/opt/openFrameworks" ARG DEBIAN_FRONTEND=noninteractive @@ -14,15 +14,22 @@ RUN apt-get update; \ lsb-release \ libmpg123-dev \ gstreamer1.0 \ - gstreamer1.0-plugins-ugly + gstreamer1.0-plugins-ugly \ + ccache \ + vim \ + unzip \ + rsync \ + git \ + ca-certificates -# Install OpenFrameworks -# based off https://openframeworks.cc/setup/raspberrypi/raspberry-pi-getting-started/ -RUN wget --no-check-certificate http://openframeworks.cc/versions/v${OF_VERSION}/of_v${OF_VERSION}_linux64_release.tar.gz && \ - mkdir -p /opt/openFrameworks && \ - tar -xzvf of_v${OF_VERSION}_linux64_release.tar.gz -C /opt/openFrameworks --strip-components 1 && \ +# Install openFrameworks for linux64 and linuxarmv6l +RUN git clone https://github.com/lucasrangit/openFrameworks.git --branch ${OF_VERSION} --single-branch /opt/openFrameworks && \ + /opt/openFrameworks/scripts/linux/download_libs.sh && \ /opt/openFrameworks/scripts/linux/ubuntu/install_dependencies.sh -y && \ - /opt/openFrameworks/scripts/linux/compileOF.sh -j3 + /opt/openFrameworks/scripts/linux/compileOF.sh -j`nproc` && \ + /opt/openFrameworks/scripts/linux/download_libs.sh --platform linuxarmv6l && \ + /opt/openFrameworks/scripts/ci/linuxarmv6l/install.sh && \ + TARGET=linuxarmv6l /opt/openFrameworks/scripts/ci/linuxarmv6l/build.sh # Add user that matches host user ARG BUILD_UID=1000 @@ -33,9 +40,7 @@ RUN (test $(getent group $BUILD_GID) || addgroup -gid $BUILD_GID docker) && \ echo '%sudo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers.d/docker # Build needs write access to /opt/openFrameworks/addons/obj for some reason -RUN mkdir /opt/openFrameworks/addons/obj && \ - chown docker. /opt/openFrameworks/addons/obj +RUN chown docker. /opt/openFrameworks/addons/obj USER $BUILD_UID WORKDIR /home/docker/hackandtell-raspberry - diff --git a/README.md b/README.md index 45d62fe..3f80eee 100644 --- a/README.md +++ b/README.md @@ -13,15 +13,17 @@ Needs http://openframeworks.cc/ to compile. make && make run # or use XCode or other IDE to compile -## Host Build with Docker +## Build + +* Pass the `--shell` argument to get an interactive shell inside the build container. +* Pass the `armv6` argument to build for the armv6 (e.g. Raspberry Pi). * `./build.sh` ### Runtime Dependencies -* Ubuntu: `sudo apt install libfreeimage3` +* Ubuntu 16.04: `sudo apt install libfreeimage3 libglfw3 liburiparser1` ### Run * `bin/hackandtell-raspberry` - diff --git a/build.sh b/build.sh index a7b362d..6cb813f 100755 --- a/build.sh +++ b/build.sh @@ -6,13 +6,18 @@ set -o xtrace if ! grep -c docker /proc/1/cgroup > /dev/null; then image_tag="$(basename $PWD | sed 's/_/-/g' | cut -c 1-40 | tr '[:upper:]' '[:lower:]'):$(git rev-parse --short HEAD)" docker build --build-arg BUILD_UID=$(id -u) --build-arg BUILD_GID=$(id -g) --tag local/$image_tag . - if [[ $1 == "--shell" ]]; then - docker run --name $(basename $PWD) -it -v $PWD:/home/docker/$(basename $PWD) local/$image_tag bash + if [[ ${1} == "--shell" ]]; then + docker run --name $(basename $PWD) -it --rm -v $PWD:/home/docker/$(basename $PWD) local/$image_tag bash else - docker run --name $(basename $PWD) --rm -v $PWD:/home/docker/$(basename $PWD) local/$image_tag $0 $* + docker run --name $(basename $PWD) --rm -v $PWD:/home/docker/$(basename $PWD) local/$image_tag "$0" "$*" fi exit $? fi -make +rm -f bin/hackandtell-raspberry +if [[ ${1} == "armv6" ]]; then + source cross.env +fi + +make -j`nproc` diff --git a/cross.env b/cross.env new file mode 100644 index 0000000..8978c59 --- /dev/null +++ b/cross.env @@ -0,0 +1,15 @@ +export TARGET=linuxarmv6l +export GCC_PREFIX=arm-linux-gnueabihf +export GST_VERSION=1.0 +export RPI_ROOT=${OF_ROOT}/scripts/ci/$TARGET/raspbian +export TOOLCHAIN_ROOT=${OF_ROOT}/scripts/ci/$TARGET/rpi_toolchain +export PLATFORM_OS=Linux +export PLATFORM_ARCH=armv6l +export PKG_CONFIG_LIBDIR=${RPI_ROOT}/usr/lib/pkgconfig:${RPI_ROOT}/usr/lib/${GCC_PREFIX}/pkgconfig:${RPI_ROOT}/usr/share/pkgconfig +export CXX="ccache ${TOOLCHAIN_ROOT}/bin/${GCC_PREFIX}-g++" +export CC="ccache ${TOOLCHAIN_ROOT}/bin/${GCC_PREFIX}-gcc" +export AR=${TOOLCHAIN_ROOT}/bin/${GCC_PREFIX}-ar +export LD=${TOOLCHAIN_ROOT}/bin/${GCC_PREFIX}-ld + +# match the flags used the build OF or it will require a recompile +export CXXFLAGS="${CXXFLAGS} -ftrack-macro-expansion=0" diff --git a/src/ofApp.cpp b/src/ofApp.cpp index 3b6e818..8799900 100644 --- a/src/ofApp.cpp +++ b/src/ofApp.cpp @@ -1,6 +1,9 @@ #include #include +#include #include +#include +#include #include "ofApp.h" //-------------------------------------------------------------- @@ -13,6 +16,8 @@ void ofApp::setup(){ #endif ofSetVerticalSync(true); resetButton.addListener(this,&ofApp::resetButtonPressed); + + ofSetEscapeQuitsApp(false); matelightFont.loadFont("OSP-DIN.ttf", 68, true, true, true); signsFont.loadFont("OSP-DIN.ttf", 100, true, true, true ); @@ -30,9 +35,11 @@ void ofApp::setup(){ gui.add(paused.setup("Pause (p/space)", true)); gui.add(minutes.setup("Minutes", 5, 1, 120)); gui.add(showApplause.setup("Show applause (a)", false)); + gui.add(showWinners.setup("Show winners (w)", false)); + gui.add(showCount.setup("Show vote count (v)", false)); gui.add(showMatelightPreview.setup("Show ML preview", false)); - + millisecondsTotal = minutes; millisecondsLeft = minutes * 60 * 1000; @@ -44,49 +51,63 @@ void ofApp::setup(){ udpConnection.Connect("10.0.1.39", 1337); udpConnection.SetNonBlocking(true); + ofRegisterURLNotification(this); } void ofApp::resetButtonPressed() { paused = true; showApplause = false; millisecondsLeft = minutes * 60 * 1000; + ofRemoveAllURLRequests(); + ofStopURLLoader(); + showWinners = false; + showCount = false; + queueGetCount = false; } //-------------------------------------------------------------- void ofApp::update(){ - if (!showApplause) { - updateTimeLeft(); - } - else { - paused = true; + std::string s1; + + updateTimeLeft(); + + if (showWinners) { + s1 = winners; + showCount = false; + } else if (showCount) { + s1 = count; + // queue another if the last update was successful + if (queueGetCount) { + queueGetCount = false; + ofLoadURLAsync("http://localhost:80/count", "async_req"); + } + } else { + // update time left display + int minutes = millisecondsLeft / 1000 / 60; + int seconds = (millisecondsLeft - (minutes * 60 * 1000)) / 1000; + int millis = millisecondsLeft - (minutes * 1000 * 60) - (seconds * 1000); + + std::stringstream ss; + ss << setfill('0') << setw(2) << seconds; + std::stringstream ms; + ms << setfill('0') << setw(3) << millis; + + s1 = ofToString(minutes) + ":" + ofToString(ss.str()); + std::string s2 = s1 + "." + ofToString(ms.str()); + + strncpy(timeLeftStr, s2.c_str(), sizeof(timeLeftStr)); + // update the local time + std::time_t t = std::time(NULL); + std::strftime(localTimeStr, sizeof(localTimeStr), "%H:%M:%S", std::localtime(&t)); } - // update time left display - int minutes = millisecondsLeft / 1000 / 60; - int seconds = (millisecondsLeft - (minutes * 60 * 1000)) / 1000; - int millis = millisecondsLeft - (minutes * 1000 * 60) - (seconds * 1000); - - std::stringstream ss; - ss << setfill('0') << setw(2) << seconds; - std::stringstream ms; - ms << setfill('0') << setw(3) << millis; - - std::string s1 = ofToString(minutes) + ":" + ofToString(ss.str()); - std::string s2 = s1 + "." + ofToString(ms.str()); - - - strncpy(timeLeftStr, s2.c_str(), sizeof(timeLeftStr)); - // update the local time - std::time_t t = std::time(NULL); - std::strftime(localTimeStr, sizeof(localTimeStr), "%H:%M:%S", std::localtime(&t)); - updateMatelight(s1); } //-------------------------------------------------------------- void ofApp::updateTimeLeft(){ - if (showApplause) { + if (showApplause || showWinners) { return; } if (paused) { @@ -145,6 +166,35 @@ void ofApp::updateMatelight(std::string text) { udpConnection.Send(message, 1924); } +void ofApp::urlResponse(ofHttpResponse & response) { + if (response.status == 200 && response.request.name == "async_req") { + std::string s = response.data.getText(); + if (response.request.url.find("winners") != std::string::npos) { + std::regex regex("[0-9]+"); + winners.clear(); + for (std::sregex_iterator i = std::sregex_iterator(s.begin(), s.end(), regex); i != std::sregex_iterator(); ++i) { + std::smatch match = *i; + winners += match.str() + " "; + } + // if there are no winners this will show a blank screen + // allowing the game master to retry getting the winners + showWinners = true; + } else if (response.request.url.find("count") != std::string::npos) { + cerr << "Count: " << s << endl; + std::stringstream stream; + stream << "0x" << setfill('0') << setw(2) << std::hex << std::stoi(s); + count = stream.str(); + // queue an update on success + queueGetCount = true; + } + } else { + // cerr << response.status << " " << response.error << endl; + // if an error occurs, clear the results in case they are no longer valid + count.clear(); + winners.clear(); + } +} + //-------------------------------------------------------------- void ofApp::drawStringMono(ofTrueTypeFont* font, std::string text, float x, float y, float w) { for (int i = 0 ; i < text.length(); i++) { @@ -215,28 +265,51 @@ void ofApp::draw(){ void ofApp::exit() { resetButton.removeListener(this,&ofApp::resetButtonPressed); + ofUnregisterURLNotification(this); } //-------------------------------------------------------------- void ofApp::keyPressed(int key){ - if (key == 'h') { + switch (key) { + case 'h': + case 'H': isMenuHidden = !isMenuHidden; - return; - } - if (key == 'a') { + break; + case 'a': + case 'A': showApplause = !showApplause; - } - if (key == 'r') { + paused = true; + break; + case 'r': + case 'R': resetButtonPressed(); - return; - } - - if (key == 'p' || key == ' ') { + break; + case 'v': + case 'V': + showCount = !showCount; + if (!showCount) + ofLoadURLAsync("http://localhost:80/count", "async_req"); + break; + case 'w': + case 'W': + // if not shown, async HTTP GET winners + if (!showWinners) + ofLoadURLAsync("http://localhost:80/winners", "async_req"); + // if shown, hide + showWinners = false; + break; + case 'p': + case 'P': + case ' ': paused = !paused; - } - - if (key == 'q') { + break; + case 'q': + case 'Q': ofExit(); + break; + default: + /* do nothing with unknown keys */ + break; } } diff --git a/src/ofApp.h b/src/ofApp.h index 2ac2a41..b8006b6 100644 --- a/src/ofApp.h +++ b/src/ofApp.h @@ -29,6 +29,7 @@ class ofApp : public ofBaseApp{ void updateTimeLeft(); void updateMatelight(std::string minutes); + void urlResponse(ofHttpResponse & response); void drawStringMono(ofTrueTypeFont* font, std::string text, float x, float y, float w); void drawApplause(); void drawCountDown(); @@ -50,21 +51,23 @@ class ofApp : public ofBaseApp{ ofPixels matelightPixels; ofPixels matelightSmall; ofImage matelightPreview; - ofxUDPManager udpConnection; bool isMenuHidden; bool isTimerRunning; ofxToggle showApplause; + ofxToggle showCount; + ofxToggle showWinners; int applauseTextColor; int applauseBackgroundColor; // buffers for variable strings on the display char localTimeStr[100]; char timeLeftStr[100]; // application state - int millisecondsTotal; int millisecondsLeft; int lastTime; - + std::string winners; + std::string count; + ofxToggle queueGetCount; };