Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

pushed to github

  • Loading branch information...
commit 416a39f16b9911b1ae334e1cb87eb1b992a100e8 0 parents
@flyingoctopus authored
2  .gitattributes
@@ -0,0 +1,2 @@
+.gitignore export-ignore
+.gitattributes export-ignore
9 .gitignore
@@ -0,0 +1,9 @@
+# compiled object files
+*.o
+
+# vim temp files
+.*swp
+
+# ./configure output
+config.mk
+.config.log
13 COPYRIGHT
@@ -0,0 +1,13 @@
+Copyright (c) 2010 William Light <wrl@illest.net>
+
+Permission to use, copy, modify, and/or distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
38 Makefile
@@ -0,0 +1,38 @@
+SHELL = /bin/sh
+
+include config.mk
+
+export CFLAGS += -ggdb -std=c99 -Wall -Werror -DVERSION=\"$(VERSION)\"
+export LDFLAGS += -L$(LIBDIR)
+export INSTALL = install
+
+.SILENT:
+.SUFFIXES:
+.SUFFIXES: .c .o
+.PHONY: all clean install config.mk
+
+all:
+ cd src; $(MAKE)
+
+clean:
+ cd src; $(MAKE) clean
+
+distclean: clean
+ rm -f config.mk
+
+install:
+ cd src; $(MAKE) install
+
+config.mk:
+ if [ ! -f config.mk ]; then \
+ echo ;\
+ echo " _ " ;\
+ echo " | |__ ___ _ _ " ;\
+ echo " | _ \ / _ \ | | | you need to run " ;\
+ echo " | | | | __/ |_| | ./configure first!" ;\
+ echo " |_| |_|\___|\__, |" ;\
+ echo " |___/ " ;\
+ echo ;\
+ fi
+
+ [ -f config.mk ]
17 README
@@ -0,0 +1,17 @@
+ _ _ _
+ ___ ___ _ __(_) __ _| | ___ ___ ___| |
+ / __|/ _ \ '__| |/ _` | |/ _ \/ __|/ __| |
+ \__ \ __/ | | | (_| | | (_) \__ \ (__|_|
+ |___/\___|_| |_|\__,_|_|\___/|___/\___(_)
+
+ serialosc is an osc server for yr monome
+
+ it is a daemon that waits for you to
+ plug a monome in, then it spawns off a
+ dedicated server process to route OSC
+ messages to and from your monome. it
+ also advertises a _monome-osc._udp
+ service over zeroconf (bonjour).
+
+ wrl@illest.net
+
652 configure
@@ -0,0 +1,652 @@
+#!/bin/bash
+
+#
+# Copyright (c) 2010 William Light <wrl@illest.net>
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+#
+
+#
+# variables and such
+#
+
+PROJECT="serialosc";
+VERSION="1.0";
+
+CC="gcc";
+LD="\$(CC)";
+CC_VERSION=;
+
+CFLAGS="-ggdb";
+LDFLAGS="-ggdb";
+
+PKGCONFIG_VERSION=;
+
+PLATFORM=;
+PLATFORM_VERSION=;
+DETECTOR=;
+PREFIX="/usr/local";
+MANDIR=;
+
+EVLOOP="poll";
+LIBUDEV_VERSION=;
+
+DNSSD_CFLAGS=;
+DNSSD_LDFLAGS="-ldns_sd";
+
+LIBMONOME_VERSION=;
+LIBMONOME_PREFIX=$PREFIX;
+LIBMONOME_CFLAGS=;
+LIBMONOME_LDFLAGS="-lmonome";
+
+LO_VERSION=;
+LO_PREFIX=$PREFIX;
+LO_CFLAGS="";
+LO_LDFLAGS="-llo -lpthread";
+
+LIBCONFUSE_VERSION=;
+LIBCONFUSE_PREFIX=$PREFIX;
+LIBCONFUSE_CFLAGS="";
+LIBCONFUSE_LDFLAGS="-lconfuse";
+
+#
+# functions
+#
+
+# figure out the right things to pass to echo to supress a newline
+if [ "`echo -n`" = "-n" ]; then
+ n=""; c="\c";
+else
+ n="-n"; c="";
+fi
+
+_echo () {
+ # portable (sysv/bsd) echo -n
+ if [ $1 = "-n" ]; then
+ shift;
+ echo $n "$*" $c;
+ else
+ echo "$*";
+ fi
+}
+
+echo_n () {
+ _echo -n "$*";
+}
+
+# color and text styles
+
+att() {
+ if [ $# -eq 0 ]; then
+ echo -ne "\033[0m";
+ return 0;
+ fi
+
+ echo -ne "\033["
+ while [ $# -ge 1 ]; do
+ case $1 in
+ bold) C=1;;
+ underline) C=4;;
+ hidden) C=8;;
+
+ black) C=30;;
+ red) C=31;;
+ green) C=32;;
+ yellow) C=33;;
+ blue) C=34;;
+ magenta) C=35;;
+ cyan) C=36;;
+ white) C=37;;
+
+ reset)
+ echo -ne "0m";
+ return 0;
+ esac
+
+ echo -ne "$C"
+
+ if [ $# -ge 2 ]; then
+ echo -ne ";";
+ fi
+
+ shift;
+ done;
+ echo -ne "m";
+}
+
+style_package () { att blue bold; _echo $*; att; }
+style_success () { att green; _echo $*; att; }
+style_distro () { att cyan; _echo $*; att; }
+style_error () { att red bold; _echo $*; att; }
+style_warning () { att yellow bold; echo $*; att; }
+style_link () { att blue bold underline; _echo $*; att; }
+
+cleanup () {
+ rm -rf .conftests;
+}
+
+bail() {
+ cleanup;
+ exit 1;
+}
+
+die() {
+ style_error $*;
+ bail;
+}
+
+# feature test commands
+
+try_command () {
+ if $* > /dev/null 2>> .config.log; then
+ return 0;
+ else
+ return 1;
+ fi
+}
+
+compile_and_run() {
+ FILE=$1
+ shift;
+
+ CMD="$CC $CFLAGS .conftests/$FILE.c -o .conftests/$FILE $LDFLAGS $*"
+ try_command $CMD;
+ RET=$?;
+
+ rm -f .conftests/$FILE.c;
+
+ [ $RET -ne 0 ] && return 1;
+
+ CMD="./.conftests/$FILE";
+ try_command $CMD;
+ RET=$?;
+
+ rm -f .conftests/$FILE;
+
+ [ $RET -ne 0 ] && return 2;
+ return 0;
+}
+
+check_pkgconfig () {
+ CMD="pkg-config --version";
+
+ if try_command $CMD; then
+ PKGCONFIG_VERSION=`$CMD`;
+ fi
+}
+
+get_platform () {
+ try_command "uname";
+
+ if [ $? -ne 0 ]; then
+ PLATFORM="dummy";
+ return 1;
+ fi
+
+ PLATFORM=`uname`;
+ return 0;
+}
+
+check_gcc () {
+ CMD="$CC -dumpversion";
+ try_command $CMD;
+
+ if [ $? -eq 0 ]; then
+ CC_VERSION=`$CMD`;
+ return 0;
+ fi
+
+ CC_VERSION=;
+ return 1;
+}
+
+check_poll () {
+ # ganked from glib's poll test
+
+ cat <<EOF > .conftests/poll.c
+#include <stdlib.h>
+#include <poll.h>
+
+int main(int argc, char **argv) {
+ struct pollfd fds[1];
+
+ fds[0].fd = open("/dev/null", 1);
+ fds[0].events = POLLIN;
+
+ if( poll(fds, 1, 0) < 0 || fds[0].revents & POLLNVAL )
+ exit(1);
+ exit(0);
+}
+EOF
+ compile_and_run poll;
+ RET=$?;
+
+ [ $RET -ne 0 ] && return $RET;
+
+ return 0;
+}
+
+check_libudev () {
+ cat <<EOF > .conftests/libudev.c
+#include <libudev.h>
+
+int main(int argc, char **argv) {
+ struct udev *udev = udev_new();
+ udev_unref(udev);
+
+ return 0;
+}
+EOF
+
+ compile_and_run libudev "" "-ludev";
+ RET=$?;
+
+ [ $RET -ne 0 ] && return $RET;
+
+ if [ $PKGCONFIG_VERSION ]; then
+ CMD="pkg-config --modversion libudev"
+ LIBUDEV_VERSION=`$CMD 2> /dev/null`;
+ fi
+
+ return 0;
+}
+
+check_dnssd() {
+ cat <<EOF > .conftests/dnssd.c
+#include <dns_sd.h>
+
+int main(int argc, char **argv) {
+ DNSServiceRef ref;
+ return 0;
+}
+EOF
+
+ compile_and_run dnssd $DNSSD_CFLAGS $DNSSD_LDFLAGS;
+ RET=$?;
+
+ [ $RET -ne 0 ] && return $RET;
+ return 0;
+}
+
+check_lo () {
+ cat <<EOF > .conftests/lo.c
+#include <lo/lo.h>
+
+int main(int argc, char **argv) {
+ lo_address a = lo_address_new(NULL, NULL);
+
+ if( !a )
+ return 1;
+
+ lo_address_free(a);
+ return 0;
+}
+EOF
+
+ compile_and_run lo $LO_CFLAGS $LO_LDFLAGS;
+ RET=$?;
+
+ [ $RET -ne 0 ] && return $RET;
+
+ if [ $PKGCONFIG_VERSION ]; then
+ CMD="pkg-config --modversion liblo"
+ LO_VERSION=`$CMD 2> /dev/null`;
+
+ [ $? -ne 0 ] && return 0;
+ fi
+
+ return 0;
+}
+
+check_libconfuse () {
+ cat <<EOF > .conftests/libconfuse.c
+#include <confuse.h>
+
+int main(int argc, char **argv) {
+ cfg_t *cfg;
+ cfg_opt_t opts[] = {
+ CFG_END()
+ };
+
+ if( !(cfg = cfg_init(opts, CFGF_NONE)) )
+ return 1;
+
+ cfg_free(cfg);
+
+ return 0;
+}
+EOF
+
+ compile_and_run libconfuse $LIBCONFUSE_CFLAGS $LIBCONFUSE_LDFLAGS;
+ RET=$?;
+
+ [ $RET -ne 0 ] && return $RET;
+
+ if [ $PKGCONFIG_VERSION ]; then
+ CMD="pkg-config --modversion libconfuse"
+ LIBCONFUSE_VERSION=`$CMD 2> /dev/null`
+
+ [ $? -ne 0 ] && return 0;
+ fi
+
+ return 0;
+}
+
+check_libmonome () {
+ cat <<EOF > .conftests/monome.c
+#include <monome.h>
+
+int main(int argc, char **argv) {
+ monome_open(NULL);
+ return 0;
+}
+EOF
+
+ compile_and_run monome $LIBMONOME_CFLAGS $LIBMONOME_LDFLAGS;
+ RET=$?;
+
+ [ $RET -ne 0 ] && return $RET;
+
+ if [ $PKGCONFIG_VERSION ]; then
+ CMD="pkg-config --modversion libmonome"
+ LIBMONOME_VERSION=`$CMD 2> /dev/null`
+
+ [ $? -ne 0 ] && return 0;
+ fi
+
+ return 0;
+}
+
+fail_if_not_dir () {
+ if [ ! -d $* ]; then
+ echo_n " "
+ style_error -n $*
+ echo " is not a valid directory."
+ echo ""
+ exit 1
+ fi
+}
+
+usage () {
+ echo ""
+ echo " usage: ./configure [OPTIONS]"
+ echo ""
+ echo " options [and defaults] are:"
+ echo " --prefix=DIR install files in PREFIX [/usr/local]"
+ echo ""
+ echo " --lo-prefix=DIR the prefix under which liblo is installed [/usr/local]"
+ echo " --libmonome-prefix=DIR the prefix under which libmonome is installed [/usr/local]"
+ echo ""
+}
+
+#
+# parse args
+#
+
+while [ -n "$1" ]; do
+ arg=`expr "$1" : "[^=]*=\(.*\)"`
+
+ case $1 in
+ --help|-h)
+ usage;
+ exit 1
+ ;;
+
+ --prefix=*)
+ fail_if_not_dir $arg;
+ PREFIX=$arg
+ ;;
+
+ --lo-prefix=*)
+ fail_if_not_dir $arg;
+ LO_PREFIX=$arg
+ ;;
+
+ --libmonome-prefix=*)
+ fail_if_not_dir $arg;
+ LIBMONOME_PREFIX=$arg
+ ;;
+
+ esac
+
+ shift;
+done;
+
+CFLAGS="$CFLAGS -I$PREFIX/include"
+LDFLAGS="$LDFLAGS -L$PREFIX/lib"
+
+LO_CFLAGS="-I$LO_PREFIX/include $LO_CFLAGS"
+LO_LDFLAGS="-L$LO_PREFIX/lib -Wl,-rpath,$LO_PREFIX/lib $LO_LDFLAGS"
+
+LIBMONOME_CFLAGS="-I$LIBMONOME_PREFIX/include $LIBMONOME_CFLAGS"
+LIBMONOME_LDFLAGS="-L$LIBMONOME_PREFIX/lib -Wl,-rpath,$LIBMONOME_PREFIX/lib $LIBMONOME_LDFLAGS"
+
+#
+# main course
+#
+
+echo "";
+mkdir -p .conftests
+rm -f .config.log
+check_pkgconfig;
+
+att bold;
+echo_n " configuring "; style_package -n $PROJECT; att; att bold; echo " ${VERSION}:";
+att;
+
+echo_n " checking platform: ";
+get_platform;
+
+case $PLATFORM in
+ Linux)
+ LIBSUFFIX=so;
+ PLATFORM_VERSION=`uname -r`;
+
+ LDFLAGS="-Wl,-rpath,\$(LIBDIR) $LDFLAGS"
+ ;;
+
+ Darwin)
+ LIBSUFFIX=dylib;
+ DETECTOR=iokitlib;
+
+ CFLAGS="$CFLAGS -DDARWIN";
+ LDFLAGS="$LDFLAGS -framework IOKit -framework CoreFoundation";
+ PLATFORM_VERSION=`expr "$(sw_vers 2>&1)" : "[^0-9]*\([0-9.]*\)"`;
+
+ export PKG_CONFIG_PATH=/usr/lib/pkgconfig:/usr/local/lib/pkgconfig:/opt/local/lib/pkgconfig:$PKG_CONFIG_PATH
+ ;;
+
+ MINGW*|mingw*)
+ LIBSUFFIX=dll;
+ PLATFORM=windows;
+ DETECTOR=windows;
+ EVLOOP=windows;
+
+ CFLAGS="$CFLAGS -DWIN32 -fms-extensions";
+
+ LO_LDFLAGS="$LO_LDFLAGS -lws2_32";
+ LIBCONFUSE_LDFLAGS="$LIBCONFUSE_LDFLAGS -lintl";
+
+ DNSSD_LDFLAGS="-ldnssd";
+ ;;
+
+ *)
+ style_error $PLATFORM;
+
+ echo "";
+ echo " Your platform is not currently supported by ${PROJECT}! I'm sorry. :(";
+ echo "";
+
+ bail;
+ ;;
+esac
+
+PLATFORM=`echo $PLATFORM | tr A-Z a-z`
+style_success "$PLATFORM $PLATFORM_VERSION"
+
+echo_n " checking for gcc: ";
+if check_gcc; then
+ style_success "gcc $CC_VERSION";
+else
+ style_error "no gcc :(";
+
+ echo "";
+ echo " I couldn't find gcc on your system! you will need it to compile ${PROJECT}.";
+ echo_n " If you're on "; style_distro -n "Ubuntu"; echo_n ", make sure to grab the "; style_package -n "build-essential"; echo " package.";
+ echo " On Mac OS X, you'll need to install the Xcode developer tools or MacPorts.";
+ echo "";
+
+ bail;
+fi
+
+conf_poll() {
+ echo_n " checking if poll() works: ";
+ if check_poll; then
+ style_success "fo sho";
+ else
+ echo "it's janky";
+ CFLAGS="$CFLAGS -DHAVE_BROKEN_POLL";
+ EVLOOP="select";
+ fi
+}
+
+conf_libudev() {
+ echo_n " checking for libudev: ";
+ if check_libudev; then
+ style_success "libudev $LIBUDEV_VERSION";
+ DETECTOR=libudev;
+ LDFLAGS="$LDFLAGS -ludev";
+ else
+ # why the fuck would you run a system this old
+
+ style_error "no, your system is outdated"
+ bail;
+ fi
+}
+
+conf_dnssd() {
+ echo_n " checking for libdns_sd: ";
+ if check_dnssd; then
+ style_success "indeed";
+ CFLAGS="$CFLAGS $DNSSD_CFLAGS";
+ LDFLAGS="$LDFLAGS $DNSSD_LDFLAGS";
+ else
+ die "no mdns";
+ fi
+}
+
+conf_liblo() {
+ echo_n " checking for liblo: ";
+ if check_lo; then
+ style_success "liblo $LO_VERSION";
+ else
+ style_error "no liblo :(";
+
+ echo "";
+ echo " I couldn't find liblo on your system!";
+ echo "";
+ echo_n " If you're "; att bold; echo_n sure; att; echo " you want to build without OSC, rerun ./configure";
+ echo_n " with the "; att bold cyan; echo_n "--disable-osc"; att; echo " option."
+ echo "";
+ echo_n " If you're on "; style_distro -n "Ubuntu"; echo_n ", make sure you have the "; style_package -n "liblo0-dev "; echo " package installed.";
+ echo_n " Users of "; style_distro -n "other distros"; echo_n " and "; style_distro -n "Mac OS X"; echo_n " should look for a "; style_package -n "liblo"; echo " package, or";
+ echo_n " get the source from the liblo homepage at "; style_link -n "http://liblo.sourceforge.net/"; echo ".";
+ echo "";
+
+ bail;
+ fi
+}
+
+conf_libmonome() {
+ echo_n " checking for libmonome: ";
+
+ if check_libmonome; then
+ style_success "libmonome $LIBMONOME_VERSION";
+ else
+ die "no libmonome :(";
+ fi
+}
+
+conf_libconfuse() {
+ echo_n " checking for libconfuse: ";
+
+ if check_libconfuse; then
+ style_success "libconfuse $LIBCONFUSE_VERSION";
+ else
+ die "no libconfuse :(";
+ fi
+}
+
+if [ $PLATFORM != "windows" ]; then
+ conf_poll;
+fi
+
+echo ""
+
+if [ $PLATFORM = "linux" ]; then
+ conf_libudev;
+fi
+
+if [ $PLATFORM != "darwin" ]; then
+ conf_dnssd;
+fi
+
+conf_liblo;
+conf_libconfuse;
+conf_libmonome;
+
+echo "";
+att bold; echo " options:"; att;
+echo_n " installation prefix: ";
+att bold; echo $PREFIX; att;
+echo_n " device detector: ";
+att bold; echo $DETECTOR; att;
+
+cat <<EOF > config.mk
+export PROJECT = $PROJECT
+export VERSION = $VERSION
+
+export PREFIX = $PREFIX
+export BINDIR = $PREFIX/bin
+export LIBDIR = $PREFIX/lib
+export INCDIR = $PREFIX/include
+export MANDIR = $MANDIR
+export PKGCONFIGDIR = \$(LIBDIR)/pkgconfig
+
+export CC = $CC
+export LD = $LD
+export CFLAGS = $CFLAGS
+export LDFLAGS = $LDFLAGS
+
+export PLATFORM = $PLATFORM
+export DETECTOR = $DETECTOR
+export LIBSUFFIX = $LIBSUFFIX
+
+export EVLOOP = $EVLOOP
+
+export LO_CFLAGS = $LO_CFLAGS
+export LO_LDFLAGS = $LO_LDFLAGS
+
+export LIBMONOME_CFLAGS = $LIBMONOME_CFLAGS
+export LIBMONOME_LDFLAGS = $LIBMONOME_LDFLAGS
+
+export LIBCONFUSE_CFLAGS = $LIBCONFUSE_CFLAGS
+export LIBCONFUSE_LDFLAGS = $LIBCONFUSE_LDFLAGS
+EOF
+
+echo "";
+echo_n " run "; style_package -n "make"; echo " to compile ${PROJECT}.";
+echo "";
+
+cleanup;
+exit 0;
2  src/.gitignore
@@ -0,0 +1,2 @@
+# compiled executable
+serialosc
38 src/Makefile
@@ -0,0 +1,38 @@
+CFLAGS += -I./private $(LO_CFLAGS) $(LIBMONOME_CFLAGS) $(LIBCONFUSE_CFLAGS)
+LDFLAGS += $(LO_LDFLAGS) $(LIBMONOME_LDFLAGS) $(LIBCONFUSE_LDFLAGS)
+
+PLATFORM_linux_OBJS = platform/posix.o platform/linux.o
+PLATFORM_darwin_OBJS = platform/posix.o platform/darwin.o
+PLATFORM_windows_OBJS = platform/windows.o
+
+SERIALOSC_OBJS = serialosc.o
+SERIALOSC_OBJS += util.o
+SERIALOSC_OBJS += server.o
+SERIALOSC_OBJS += config.o
+SERIALOSC_OBJS += osc/util.o
+SERIALOSC_OBJS += osc/mext_methods.o
+SERIALOSC_OBJS += osc/sys_methods.o
+
+SERIALOSC_OBJS += $(PLATFORM_$(PLATFORM)_OBJS)
+SERIALOSC_OBJS += detector/$(DETECTOR).o
+SERIALOSC_OBJS += event_loop/$(EVLOOP).o
+
+all: serialosc
+
+install: all
+ $(INSTALL) -d $(BINDIR)
+
+ echo " INSTALL src/serialosc -> $(BINDIR)/serialosc"
+ $(INSTALL) serialosc $(BINDIR)/serialosc
+
+clean:
+ echo " CLEAN src"
+ rm -f serialosc $(SERIALOSC_OBJS)
+
+serialosc: $(SERIALOSC_OBJS)
+ echo " LD src/serialosc"
+ $(LD) -o $@ $(SERIALOSC_OBJS) $(LDFLAGS)
+
+.c.o:
+ echo " CC src/$@"
+ $(CC) $(CFLAGS) -c $< -o $@
149 src/config.c
@@ -0,0 +1,149 @@
+/**
+ * Copyright (c) 2010 William Light <wrl@illest.net>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/stat.h>
+
+#include <confuse.h>
+#include <monome.h>
+#include <lo/lo.h>
+
+#include "serialosc.h"
+
+
+#define DEFAULT_SERVER_PORT 0
+#define DEFAULT_OSC_PREFIX "/monome"
+#define DEFAULT_APP_PORT 8000
+#define DEFAULT_APP_HOST "127.0.0.1"
+#define DEFAULT_ROTATION MONOME_ROTATE_0
+
+
+static cfg_opt_t server_opts[] = {
+ CFG_INT("port", DEFAULT_SERVER_PORT, CFGF_NONE),
+ CFG_END()
+};
+
+static cfg_opt_t app_opts[] = {
+ CFG_STR("osc_prefix", DEFAULT_OSC_PREFIX, CFGF_NONE),
+ CFG_STR("host", DEFAULT_APP_HOST, CFGF_NONE),
+ CFG_INT("port", DEFAULT_APP_PORT, CFGF_NONE),
+ CFG_END()
+};
+
+static cfg_opt_t dev_opts[] = {
+ CFG_INT("rotation", DEFAULT_ROTATION, CFGF_NONE),
+ CFG_END()
+};
+
+static cfg_opt_t opts[] = {
+ CFG_SEC("server", server_opts, CFGF_NONE),
+ CFG_SEC("application", app_opts, CFGF_NONE),
+ CFG_SEC("device", dev_opts, CFGF_NONE),
+ CFG_END()
+};
+
+
+static void prepend_slash_if_necessary(char **dest, char *prefix) {
+ if( *prefix != '/' )
+ *dest = s_asprintf("/%s", prefix);
+ else
+ *dest = s_strdup(prefix);
+}
+
+static char *path_for_serial(const char *serial) {
+ char *path, *cdir;
+
+ cdir = sosc_get_config_directory();
+ path = s_asprintf("%s/%s.conf", cdir, serial);
+
+ s_free(cdir);
+ return path;
+}
+
+int sosc_config_read(const char *serial, sosc_config_t *config) {
+ cfg_t *cfg, *sec;
+ char *path;
+
+ if( !serial )
+ return 1;
+
+ cfg = cfg_init(opts, CFGF_NOCASE);
+ path = path_for_serial(serial);
+
+ switch( cfg_parse(cfg, path) ) {
+ case CFG_PARSE_ERROR:
+ fprintf(stderr, "serialosc [%s]: parse error in saved configuration\n",
+ serial);
+ break;
+ }
+
+ s_free(path);
+
+ sec = cfg_getsec(cfg, "server");
+ sosc_port_itos(config->server.port, cfg_getint(sec, "port"));
+
+ sec = cfg_getsec(cfg, "application");
+ prepend_slash_if_necessary(&config->app.osc_prefix, cfg_getstr(sec, "osc_prefix"));
+ config->app.host = s_strdup(cfg_getstr(sec, "host"));
+ sosc_port_itos(config->app.port, cfg_getint(sec, "port"));
+
+ sec = cfg_getsec(cfg, "device");
+ config->dev.rotation = (cfg_getint(sec, "rotation") / 90) % 4;
+
+ cfg_free(cfg);
+
+ return 0;
+}
+
+int sosc_config_write(const char *serial, sosc_state_t *state) {
+ cfg_t *cfg, *sec;
+ char *path;
+ const char *p;
+ FILE *f;
+
+ if( !serial )
+ return 1;
+
+ cfg = cfg_init(opts, CFGF_NOCASE);
+
+ path = path_for_serial(serial);
+ if( !(f = fopen(path, "w")) ) {
+ s_free(path);
+ return 1;
+ }
+
+ s_free(path);
+
+ sec = cfg_getsec(cfg, "server");
+ cfg_setint(sec, "port", lo_server_get_port(state->server));
+
+ sec = cfg_getsec(cfg, "application");
+ cfg_setstr(sec, "osc_prefix", state->config.app.osc_prefix);
+ cfg_setstr(sec, "host", lo_address_get_hostname(state->outgoing));
+ p = lo_address_get_port(state->outgoing);
+ cfg_setint(sec, "port", strtol(p , NULL, 10));
+
+ sec = cfg_getsec(cfg, "device");
+ cfg_setint(sec, "rotation", monome_get_rotation(state->monome) * 90);
+
+ cfg_print(cfg, f);
+ fclose(f);
+
+ return 0;
+}
156 src/detector/iokitlib.c
@@ -0,0 +1,156 @@
+/**
+ * Copyright (c) 2010 William Light <wrl@illest.net>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <signal.h>
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <IOKit/IOKitLib.h>
+#include <IOKit/usb/IOUSBLib.h>
+#include <IOKit/serial/IOSerialKeys.h>
+
+#include <monome.h>
+
+#include "serialosc.h"
+
+
+typedef struct {
+ IONotificationPortRef notify;
+ io_iterator_t iter;
+
+ const char *exec_path;
+} notify_state_t;
+
+
+static void disable_subproc_waiting() {
+ struct sigaction s;
+
+ memset(&s, 0, sizeof(struct sigaction));
+ s.sa_flags = SA_NOCLDWAIT;
+ s.sa_handler = SIG_IGN;
+
+ if( sigaction(SIGCHLD, &s, NULL) < 0 ) {
+ perror("disable_subproc_waiting");
+ exit(EXIT_FAILURE);
+ }
+}
+
+static int spawn_server(const char *exec_path, const char *devnode) {
+ switch( fork() ) {
+ case 0: break;
+ case -1: perror("spawn_server fork"); return 1;
+ default: return 0;
+ }
+
+ execlp(exec_path, exec_path, devnode, NULL);
+
+ /* only get here if an error occurs */
+ perror("spawn_server execlp");
+ return 1;
+}
+
+static int wait_on_parent_usbdevice(io_service_t device) {
+ io_registry_entry_t parent;
+
+ /* walk up the device tree looking for the IOUSBDevice */
+ for( ;; ) {
+ /* return an error if we've walked off the end of the tree,
+ i.e. if this tty isn't a USB device. */
+ if( IORegistryEntryGetParentEntry(device, kIOServicePlane, &parent) )
+ return 1;
+ device = parent;
+
+ if( IOObjectConformsTo(device, kIOUSBDeviceClassName) )
+ break;
+ }
+
+ /* wait until the device is ready to be opened */
+ IOServiceWaitQuiet(device, NULL);
+ return 0;
+}
+
+static void iterate_devices(void *context, io_iterator_t iter) {
+ notify_state_t *state = context;
+
+ io_service_t device;
+ io_struct_inband_t devnode;
+ unsigned int len = 256;
+
+ while( (device = IOIteratorNext(iter)) ) {
+ IORegistryEntryGetProperty(device, kIODialinDeviceKey, devnode, &len);
+
+ if( !wait_on_parent_usbdevice(device) )
+ spawn_server(state->exec_path, devnode);
+
+ IOObjectRelease(device);
+ }
+
+ return;
+}
+
+static int init_iokitlib(notify_state_t *state) {
+ CFMutableDictionaryRef matching;
+
+ if( !(state->notify = IONotificationPortCreate((mach_port_t) 0)) ) {
+ fprintf(stderr, "couldn't allocate notification port, aieee!\n");
+ return 1;
+ }
+
+ CFRunLoopAddSource(
+ /* run loop */ CFRunLoopGetCurrent(),
+ /* source */ IONotificationPortGetRunLoopSource(state->notify),
+ /* mode */ kCFRunLoopDefaultMode);
+
+ matching = IOServiceMatching(kIOSerialBSDServiceValue);
+ CFDictionarySetValue(matching,
+ CFSTR(kIOSerialBSDTypeKey),
+ CFSTR(kIOSerialBSDRS232Type));
+
+ IOServiceAddMatchingNotification(
+ /* notify port */ state->notify,
+ /* notification type */ kIOMatchedNotification,
+ /* matching dict */ matching,
+ /* callback */ iterate_devices,
+ /* callback context */ state,
+ /* iterator */ &state->iter);
+
+ return 0;
+}
+
+static void fini_iokitlib(notify_state_t *state) {
+ IOObjectRelease(state->iter);
+ IONotificationPortDestroy(state->notify);
+}
+
+int detector_run(const char *exec_path) {
+ notify_state_t state;
+
+ assert(exec_path);
+ state.exec_path = exec_path;
+
+ disable_subproc_waiting();
+ if( init_iokitlib(&state) )
+ return 1;
+
+ iterate_devices(&state, state.iter);
+ CFRunLoopRun();
+
+ fini_iokitlib(&state);
+ return 0;
+}
153 src/detector/libudev.c
@@ -0,0 +1,153 @@
+/**
+ * Copyright (c) 2010 William Light <wrl@illest.net>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#define _XOPEN_SOURCE 600
+
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <unistd.h>
+#include <errno.h>
+#include <poll.h>
+
+#include <libudev.h>
+#include <monome.h>
+
+#include "serialosc.h"
+
+
+typedef struct {
+ struct udev *u;
+ struct udev_monitor *um;
+
+ const char *exec_path;
+} detector_state_t;
+
+
+static void disable_subproc_waiting() {
+ struct sigaction s;
+
+ memset(&s, 0, sizeof(struct sigaction));
+ s.sa_flags = SA_NOCLDWAIT;
+ s.sa_handler = SIG_IGN;
+
+ if( sigaction(SIGCHLD, &s, NULL) < 0 ) {
+ perror("disable_subproc_waiting");
+ exit(EXIT_FAILURE);
+ }
+}
+
+static int spawn_server(const char *exec_path, const char *devnode) {
+ switch( fork() ) {
+ case 0: break;
+ case -1: perror("spawn_server fork"); return 1;
+ default: return 0;
+ }
+
+ execlp(exec_path, exec_path, devnode, NULL);
+
+ /* only get here if an error occurs */
+ perror("spawn_server execlp");
+ return 1;
+}
+
+static monome_t *monitor_attach(detector_state_t *state) {
+ struct udev_device *ud;
+ struct pollfd fds[1];
+
+ fds[0].fd = udev_monitor_get_fd(state->um);
+ fds[0].events = POLLIN;
+
+ do {
+ if( poll(fds, 1, -1) < 0 )
+ switch( errno ) {
+ case EINVAL:
+ perror("error in poll()");
+ exit(1);
+
+ case EINTR:
+ case EAGAIN:
+ continue;
+ }
+
+ ud = udev_monitor_receive_device(state->um);
+
+ /* check if this was an add event.
+ "add"[0] == 'a' */
+ if( *(udev_device_get_action(ud)) == 'a' )
+ spawn_server(state->exec_path, udev_device_get_devnode(ud));
+
+ udev_device_unref(ud);
+ } while( 1 );
+}
+
+int scan_connected_devices(detector_state_t *state) {
+ struct udev_list_entry *cursor;
+ struct udev_enumerate *ue;
+ struct udev_device *ud;
+
+ const char *devnode = NULL;
+
+ if( !(ue = udev_enumerate_new(state->u)) )
+ return 1;
+
+ udev_enumerate_add_match_subsystem(ue, "tty");
+ udev_enumerate_add_match_property(ue, "ID_BUS", "usb");
+ udev_enumerate_scan_devices(ue);
+ cursor = udev_enumerate_get_list_entry(ue);
+
+ do {
+ ud = udev_device_new_from_syspath(
+ state->u, udev_list_entry_get_name(cursor));
+
+ if( (devnode = udev_device_get_devnode(ud)) )
+ spawn_server(state->exec_path, devnode);
+
+ udev_device_unref(ud);
+ } while( (cursor = udev_list_entry_get_next(cursor)) );
+
+ udev_enumerate_unref(ue);
+ return 0;
+}
+
+int detector_run(const char *exec_path) {
+ detector_state_t state = {
+ .exec_path = exec_path
+ };
+
+ assert(exec_path);
+ disable_subproc_waiting();
+ state.u = udev_new();
+
+ if( scan_connected_devices(&state) )
+ return 1;
+
+ if( !(state.um = udev_monitor_new_from_netlink(state.u, "udev")) )
+ return 2;
+
+ udev_monitor_filter_add_match_subsystem_devtype(state.um, "tty", NULL);
+ udev_monitor_enable_receiving(state.um);
+
+ if( monitor_attach(&state) )
+ return 3;
+
+ udev_monitor_unref(state.um);
+ udev_unref(state.u);
+
+ return 0;
+}
352 src/detector/windows.c
@@ -0,0 +1,352 @@
+/**
+ * Copyright (c) 2011 William Light <wrl@illest.net>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <assert.h>
+#include <stdio.h>
+
+/* for RegisterDeviceNotification */
+#define WINVER 0x501
+
+#include <windows.h>
+#include <process.h>
+#include <Winreg.h>
+#include <Dbt.h>
+
+#include "serialosc.h"
+
+
+#define FTDI_REG_PATH "SYSTEM\\CurrentControlSet\\Enum\\FTDIBUS"
+#define SERVICE_NAME "serialosc"
+
+typedef struct {
+ HANDLE reaper_job;
+ SERVICE_STATUS svc_status;
+ SERVICE_STATUS_HANDLE hstatus;
+
+ char *exec_path;
+ const char *quoted_exec_path;
+} detector_state_t;
+
+detector_state_t state = {
+ .svc_status = {
+ .dwServiceType = SERVICE_WIN32,
+ .dwCurrentState = SERVICE_START_PENDING,
+ .dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN,
+ .dwWin32ExitCode = 0,
+ .dwServiceSpecificExitCode = 0,
+ .dwCheckPoint = 0,
+ .dwWaitHint = 0
+ }
+};
+
+static int spawn_server(const char *devnode) {
+ intptr_t proc;
+
+ proc = _spawnlp(_P_NOWAIT, state.exec_path, state.quoted_exec_path,
+ devnode, NULL);
+
+ if( proc < 0 ) {
+ perror("dang");
+ return 1;
+ }
+
+ AssignProcessToJobObject(state.reaper_job, (HANDLE) proc);
+
+ return 0;
+}
+
+int scan_connected_devices() {
+ HKEY key, subkey;
+ char subkey_name[MAX_PATH], *subkey_path, *serial;
+ unsigned char port_name[64];
+ DWORD klen, plen, ptype;
+ int i = 0;
+
+ serial = NULL;
+
+ switch( RegOpenKeyEx(
+ HKEY_LOCAL_MACHINE, FTDI_REG_PATH,
+ 0, KEY_READ, &key) ) {
+ case ERROR_SUCCESS:
+ /* ERROR: request was (unexpectedly) successful */
+ break;
+
+ case ERROR_FILE_NOT_FOUND:
+ /* print message about needing the FTDI driver maybe? */
+ /* fall through also */
+ default:
+ return 1;
+ }
+
+ do {
+ klen = sizeof(subkey_name) / sizeof(char);
+ switch( RegEnumKeyEx(key, i++, subkey_name, &klen,
+ NULL, NULL, NULL, NULL) ) {
+ case ERROR_MORE_DATA:
+ case ERROR_SUCCESS:
+ break;
+
+ default:
+ goto done;
+ }
+
+ subkey_path = s_asprintf("%s\\%s\\0000\\Device Parameters",
+ FTDI_REG_PATH, subkey_name);
+
+ switch( RegOpenKeyEx(
+ HKEY_LOCAL_MACHINE, subkey_path,
+ 0, KEY_READ, &subkey) ) {
+ case ERROR_SUCCESS:
+ break;
+
+ default:
+ free(subkey_path);
+ continue;
+ }
+
+ free(subkey_path);
+
+ plen = sizeof(port_name) / sizeof(char);
+ ptype = REG_SZ;
+ switch( RegQueryValueEx(subkey, "PortName", 0, &ptype,
+ port_name, &plen) ) {
+ case ERROR_SUCCESS:
+ port_name[plen] = '\0';
+ break;
+
+ default:
+ goto next;
+ }
+
+ spawn_server((char *) port_name);
+
+next:
+ RegCloseKey(subkey);
+ } while( 1 );
+
+done:
+ RegCloseKey(key);
+ return 0;
+}
+
+char *get_service_binpath() {
+ SC_HANDLE manager, service;
+ LPQUERY_SERVICE_CONFIG config;
+ DWORD config_size;
+ char *bin_path;
+
+ if( !(manager = OpenSCManager(NULL, NULL, SC_MANAGER_ENUMERATE_SERVICE)) )
+ goto err_manager;
+
+ if( !(service = OpenService(manager, SERVICE_NAME, SERVICE_QUERY_CONFIG)) )
+ goto err_service;
+
+ QueryServiceConfig(service, NULL, 0, &config_size);
+
+ if( !(config = s_malloc(config_size)) )
+ goto err_malloc;
+
+ if( !QueryServiceConfig(service, config, config_size, &config_size) )
+ goto err_query;
+
+ bin_path = s_strdup(config->lpBinaryPathName);
+ s_free(config);
+
+ CloseServiceHandle(service);
+ CloseServiceHandle(manager);
+ return bin_path;
+
+err_query:
+ s_free(config);
+err_malloc:
+ CloseServiceHandle(service);
+err_service:
+ CloseServiceHandle(manager);
+err_manager:
+ return NULL;
+}
+
+char *ftdishit_to_port(char *bullshit) {
+ char *subkey_path, *port, port_name[64];
+ DWORD plen, ptype;
+ HKEY subkey;
+
+ if( !(bullshit = strchr(bullshit, '#')) )
+ return NULL;
+
+ if( !(port = strchr(++bullshit, '#')) )
+ return NULL;
+
+ *port = '\0';
+
+ subkey_path = s_asprintf(FTDI_REG_PATH "\\%s\\0000\\Device Parameters",
+ bullshit);
+
+ switch( RegOpenKeyEx(
+ HKEY_LOCAL_MACHINE, subkey_path,
+ 0, KEY_READ, &subkey) ) {
+ case ERROR_SUCCESS:
+ break;
+
+ default:
+ free(subkey_path);
+ return NULL;
+ }
+
+ free(subkey_path);
+
+ plen = sizeof(port_name) / sizeof(char);
+ ptype = REG_SZ;
+ switch( RegQueryValueEx(subkey, "PortName", 0, &ptype,
+ (unsigned char *) port_name, &plen) ) {
+ case ERROR_SUCCESS:
+ port_name[plen] = '\0';
+ break;
+
+ default:
+ RegCloseKey(subkey);
+ return NULL;
+ }
+
+ RegCloseKey(subkey);
+ return s_strdup(port_name);
+}
+
+DWORD WINAPI control_handler(DWORD ctrl, DWORD type, LPVOID data, LPVOID ctx) {
+ DEV_BROADCAST_DEVICEINTERFACE *dev;
+ char devname[256], *port;
+
+ switch( ctrl ) {
+ case SERVICE_CONTROL_SHUTDOWN:
+ case SERVICE_CONTROL_STOP:
+ state.svc_status.dwWin32ExitCode = 0;
+ state.svc_status.dwCurrentState = SERVICE_STOPPED;
+ SetServiceStatus(state.hstatus, &state.svc_status);
+ return NO_ERROR;
+
+ case SERVICE_CONTROL_INTERROGATE:
+ SetServiceStatus(state.hstatus, &state.svc_status);
+ return NO_ERROR;
+
+ case SERVICE_CONTROL_DEVICEEVENT:
+ switch( type ) {
+ case DBT_DEVICEARRIVAL:
+ dev = data;
+ wcstombs(devname, (wchar_t *) dev->dbcc_name, sizeof(devname));
+ port = ftdishit_to_port(devname);
+
+ spawn_server(port);
+
+ s_free(port);
+ break;
+
+ default:
+ break;
+ }
+ return NO_ERROR;
+
+ default:
+ break;
+ }
+
+ return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+/* damnit mingw */
+#ifndef JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE
+#define JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE 0x00002000
+#endif
+
+int setup_reaper_job() {
+ JOBOBJECT_EXTENDED_LIMIT_INFORMATION jeli;
+ memset(&jeli, '\0', sizeof(jeli));
+
+ if( !(state.reaper_job = CreateJobObject(NULL, NULL)) )
+ return 1;
+
+ jeli.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
+ if( !SetInformationJobObject(
+ state.reaper_job, JobObjectExtendedLimitInformation, &jeli,
+ sizeof(jeli)) )
+ return 1;
+
+ return 0;
+}
+
+int setup_device_notification() {
+ DEV_BROADCAST_DEVICEINTERFACE filter;
+ GUID vcp_guid = {0x86e0d1e0L, 0x8089, 0x11d0,
+ {0x9c, 0xe4, 0x08, 0x00, 0x3e, 0x30, 0x1f, 0x73}};
+
+ filter.dbcc_size = sizeof(filter);
+ filter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
+ filter.dbcc_reserved = 0;
+ filter.dbcc_classguid = vcp_guid;
+ filter.dbcc_name[0] = '\0';
+
+ if( !RegisterDeviceNotification((HANDLE) state.hstatus, &filter,
+ DEVICE_NOTIFY_SERVICE_HANDLE))
+ return 1;
+ return 0;
+}
+
+void WINAPI service_main(DWORD argc, LPTSTR *argv) {
+ state.hstatus = RegisterServiceCtrlHandlerEx(
+ SERVICE_NAME, control_handler, NULL);
+
+ if( !state.hstatus )
+ return;
+
+ state.svc_status.dwCurrentState = SERVICE_RUNNING;
+ SetServiceStatus(state.hstatus, &state.svc_status);
+
+ if( !(state.exec_path = get_service_binpath()) )
+ goto err;
+
+ if( !(state.quoted_exec_path = s_asprintf("\"%s\"", state.exec_path)) ) {
+ fprintf(stderr, "detector_run() error: couldn't allocate memory\n");
+ goto err_asprintf;
+ }
+
+ if( setup_reaper_job() )
+ goto err_rdnotification;
+
+ scan_connected_devices(&state);
+
+ if( setup_device_notification() )
+ goto err_rdnotification;
+
+ return;
+
+err_rdnotification:
+err_asprintf:
+ s_free(state.exec_path);
+err:
+ state.svc_status.dwCurrentState = SERVICE_STOPPED;
+ state.svc_status.dwWin32ExitCode = 1;
+ SetServiceStatus(state.hstatus, &state.svc_status);
+ return;
+}
+
+int detector_run(const char *exec_path) {
+ SERVICE_TABLE_ENTRY services[] = {
+ {SERVICE_NAME, service_main},
+ {NULL, NULL}
+ };
+
+ StartServiceCtrlDispatcher(services);
+ return 0;
+}
58 src/event_loop/poll.c
@@ -0,0 +1,58 @@
+/**
+ * Copyright (c) 2010 William Light <wrl@illest.net>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <poll.h>
+
+#include "serialosc.h"
+
+
+int event_loop(const sosc_state_t *state) {
+ struct pollfd fds[2];
+
+ fds[0].fd = monome_get_fd(state->monome);
+ fds[1].fd = lo_server_get_socket_fd(state->server);
+
+ fds[0].events = POLLIN;
+ fds[1].events = POLLIN;
+
+ do {
+ /* block until either the monome or liblo have data */
+ if( poll(fds, 2, -1) < 0 )
+ switch( errno ) {
+ case EINVAL:
+ perror("error in poll()");
+ return 1;
+
+ case EINTR:
+ case EAGAIN:
+ continue;
+ }
+
+ /* is the monome still connected? */
+ if( fds[0].revents & (POLLHUP | POLLERR) )
+ return 1;
+
+ /* is there data available for reading from the monome? */
+ if( fds[0].revents & POLLIN )
+ monome_event_handle_next(state->monome);
+
+ /* how about from OSC? */
+ if( fds[1].revents & POLLIN )
+ lo_server_recv_noblock(state->server, 0);
+ } while( 1 );
+}
64 src/event_loop/select.c
@@ -0,0 +1,64 @@
+/**
+ * Copyright (c) 2010 William Light <wrl@illest.net>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <sys/select.h>
+
+#include "serialosc.h"
+
+
+int event_loop(const sosc_state_t *state) {
+ fd_set rfds, efds;
+ int maxfd, mfd, lofd;
+
+ mfd = monome_get_fd(state->monome);
+ lofd = lo_server_get_socket_fd(state->server);
+ maxfd = ((lofd > mfd) ? lofd : mfd) + 1;
+
+ do {
+ FD_ZERO(&rfds);
+ FD_SET(mfd, &rfds);
+ FD_SET(lofd, &rfds);
+
+ FD_ZERO(&efds);
+ FD_SET(mfd, &efds);
+
+ /* block until either the monome or liblo have data */
+ if( select(maxfd, &rfds, NULL, &efds, NULL) < 0 )
+ switch( errno ) {
+ case EBADF:
+ case EINVAL:
+ perror("error in select()");
+ return 1;
+
+ case EINTR:
+ continue;
+ }
+
+ /* is the monome still connected? */
+ if( FD_ISSET(mfd, &efds) )
+ return 1;
+
+ /* is there data available for reading from the monome? */
+ if( FD_ISSET(mfd, &rfds) )
+ monome_event_handle_next(state->monome);
+
+ /* how about from OSC? */
+ if( FD_ISSET(lofd, &rfds) )
+ lo_server_recv_noblock(state->server, 0);
+ } while( 1 );
+}
81 src/event_loop/windows.c
@@ -0,0 +1,81 @@
+/**
+ * Copyright (c) 2011 William Light <wrl@illest.net>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdio.h>
+
+#include <windows.h>
+#include <Winsock.h>
+#include <io.h>
+
+#include "serialosc.h"
+
+static DWORD WINAPI lo_thread(LPVOID param) {
+ sosc_state_t *state = param;
+
+ while( 1 )
+ lo_server_recv(state->server);
+
+ return 0;
+}
+
+int event_loop(const sosc_state_t *state) {
+ OVERLAPPED ov = {0, 0, {{0, 0}}};
+ HANDLE hres, lo_thd_res;
+ DWORD evt_mask;
+
+ hres = (HANDLE) _get_osfhandle(monome_get_fd(state->monome));
+ lo_thd_res = CreateThread(NULL, 0, lo_thread, (void *) state, 0, NULL);
+
+ if( !(ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL)) ) {
+ fprintf(stderr, "serialosc: event_loop: can't allocate event (%ld)\n",
+ GetLastError());
+ return 1;
+ }
+
+ do {
+ SetCommMask(hres, EV_RXCHAR);
+
+ if( !WaitCommEvent(hres, &evt_mask, &ov) )
+ switch( GetLastError() ) {
+ case ERROR_IO_PENDING:
+ break;
+
+ case ERROR_ACCESS_DENIED:
+ /* evidently we get this when the monome is unplugged? */
+ return 1;
+
+ default:
+ printf("event_loop() error: %d\n", GetLastError());
+ return 1;
+ }
+
+ switch( WaitForSingleObject(ov.hEvent, INFINITE) ) {
+ case WAIT_OBJECT_0:
+ while( monome_event_handle_next(state->monome) );
+ break;
+
+ case WAIT_TIMEOUT:
+ break;
+
+ case WAIT_ABANDONED_0:
+ case WAIT_FAILED:
+ printf("event_loop(): wait failed: %ld\n", GetLastError());
+ return 1;
+ }
+ } while ( 1 );
+
+ return 0;
+}
173 src/osc/methods.c
@@ -0,0 +1,173 @@
+/**
+ * Copyright (c) 2010 William Light <wrl@illest.net>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdlib.h>
+
+#include <lo/lo.h>
+#include <monome.h>
+
+#include "serialosc.h"
+#include "osc.h"
+
+
+OSC_HANDLER_FUNC(osc_clear_handler) {
+ monome_t *monome = user_data;
+ int mode = (argc) ? argv[0]->i : 0;
+
+ return monome_clear(monome, mode);
+}
+
+OSC_HANDLER_FUNC(osc_intensity_handler) {
+ monome_t *monome = user_data;
+ int intensity = (argc) ? argv[0]->i : 0xF;
+
+ return monome_intensity(monome, intensity);
+}
+
+OSC_HANDLER_FUNC(osc_led_handler) {
+ monome_t *monome = user_data;
+ return monome_led(monome, argv[0]->i, argv[1]->i, argv[2]->i);
+}
+
+OSC_HANDLER_FUNC(osc_led_col_handler) {
+ monome_t *monome = user_data;
+ uint8_t buf[2] = {argv[1]->i};
+
+ if( argc == 3 )
+ buf[1] = argv[2]->i;
+
+ return monome_led_col(monome, argv[0]->i, 0, argc - 1, buf);
+}
+
+OSC_HANDLER_FUNC(osc_led_row_handler) {
+ monome_t *monome = user_data;
+ uint8_t buf[2] = {argv[1]->i};
+
+ if( argc == 3 )
+ buf[1] = argv[2]->i;
+
+ return monome_led_row(monome, argv[0]->i, 0, argc - 1, buf);
+}
+
+OSC_HANDLER_FUNC(osc_frame_handler) {
+ monome_t *monome = user_data;
+ uint8_t buf[8];
+ int i;
+
+ for( i = 0; i < 8; i++ )
+ buf[i] = argv[i + (argc - 8)]->i;
+
+ switch( argc ) {
+ case 8:
+ return monome_led_frame(monome, 0, 0, buf);
+
+ case 10:
+ return monome_led_frame(monome, argv[0]->i, argv[1]->i, buf);
+ }
+
+ return -1;
+}
+
+#define METHOD(path) for( cmd_buf = osc_path(path, prefix); cmd_buf; \
+ s_free(cmd_buf), cmd_buf = NULL )
+
+void osc_register_methods(sosc_state_t *state) {
+ char *prefix, *cmd_buf;
+ monome_t *monome;
+ lo_server srv;
+
+ prefix = state->config.app.osc_prefix;
+ monome = state->monome;
+ srv = state->server;
+
+#define REGISTER(typetags, cb) \
+ lo_server_add_method(srv, cmd_buf, typetags, cb, monome)
+
+ METHOD("clear") {
+ REGISTER("", osc_clear_handler);
+ REGISTER("i", osc_clear_handler);
+ }
+
+ METHOD("intensity") {
+ REGISTER("", osc_intensity_handler);
+ REGISTER("i", osc_intensity_handler);
+ }
+
+ METHOD("led")
+ REGISTER("iii", osc_led_handler);
+
+ METHOD("led_row") {
+ REGISTER("ii", osc_led_row_handler);
+ REGISTER("iii", osc_led_row_handler);
+ }
+
+ METHOD("led_col") {
+ REGISTER("ii", osc_led_col_handler);
+ REGISTER("iii", osc_led_col_handler);
+ }
+
+ METHOD("frame") {
+ REGISTER("iiiiiiii", osc_frame_handler);
+ REGISTER("iiiiiiiiii", osc_frame_handler);
+ }
+
+#undef REGISTER
+}
+
+void osc_unregister_methods(sosc_state_t *state) {
+ char *prefix, *cmd_buf;
+ monome_t *monome;
+ lo_server srv;
+
+ prefix = state->config.app.osc_prefix;
+ monome = state->monome;
+ srv = state->server;
+
+#define UNREGISTER(typetags) \
+ lo_server_del_method(srv, cmd_buf, typetags)
+
+ METHOD("clear") {
+ UNREGISTER("");
+ UNREGISTER("i");
+ }
+
+ METHOD("intensity") {
+ UNREGISTER("");
+ UNREGISTER("i");
+ }
+
+ METHOD("led")
+ UNREGISTER("iii");
+
+ METHOD("led_row") {
+ UNREGISTER("ii");
+ UNREGISTER("iii");
+ }
+
+ METHOD("led_col") {
+ UNREGISTER("ii");
+ UNREGISTER("iii");
+ }
+
+ METHOD("frame") {
+ UNREGISTER("iiiiiiii");
+ UNREGISTER("iiiiiiiiii");
+ }
+
+#undef UNREGISTER
+}
+
+#undef METHOD
344 src/osc/mext_methods.c
@@ -0,0 +1,344 @@
+/**
+ * Copyright (c) 2011 William Light <wrl@illest.net>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdlib.h>
+
+#include <lo/lo.h>
+#include <monome.h>
+
+#include "serialosc.h"
+#include "osc.h"
+
+
+OSC_HANDLER_FUNC(led_set_handler) {
+ monome_t *monome = user_data;
+ return monome_led_set(monome, argv[0]->i, argv[1]->i, !!argv[2]->i);
+}
+
+OSC_HANDLER_FUNC(led_all_handler) {
+ monome_t *monome = user_data;
+ return monome_led_all(monome, !!argv[0]->i);
+}
+
+OSC_HANDLER_FUNC(led_map_handler) {
+ monome_t *monome = user_data;
+ uint8_t buf[8];
+ int i;
+
+ for( i = 0; i < 8; i++ )
+ buf[i] = argv[i + (argc - 8)]->i;
+
+ return monome_led_map(monome, argv[0]->i, argv[1]->i, buf);
+}
+
+OSC_HANDLER_FUNC(led_col_handler) {
+ monome_t *monome = user_data;
+ uint8_t buf[32];
+ int i;
+
+ if( argc < 3 || argc > 34 )
+ return 1;
+
+ for( i = 0; i < argc; i++ )
+ if( types[i] != 'i' ) /* only integers are invited to this party */
+ return 1;
+
+ for( i = 0; i < (argc - 2); i++ )
+ buf[i] = argv[i + 2]->i;
+
+ return monome_led_col(monome, argv[0]->i, argv[1]->i, argc - 2, buf);
+}
+
+OSC_HANDLER_FUNC(led_row_handler) {
+ monome_t *monome = user_data;
+ uint8_t buf[32];
+ int i;
+
+ if( argc < 3 || argc > 34 )
+ return 1;
+
+ for( i = 0; i < argc; i++ )
+ if( types[i] != 'i' )
+ return 1;
+
+ for( i = 0; i < (argc - 2); i++ )
+ buf[i] = argv[i + 2]->i;
+
+ return monome_led_row(monome, argv[0]->i, argv[1]->i, argc - 2, buf);
+}
+
+OSC_HANDLER_FUNC(led_intensity_handler) {
+ monome_t *monome = user_data;
+ return monome_led_intensity(monome, argv[0]->i);
+}
+
+OSC_HANDLER_FUNC(led_level_set_handler) {
+ monome_t *monome = user_data;
+ return monome_led_level_set(monome, argv[0]->i, argv[1]->i, argv[2]->i);
+}
+
+OSC_HANDLER_FUNC(led_level_all_handler) {
+ monome_t *monome = user_data;
+ return monome_led_level_all(monome, argv[0]->i);
+}
+
+OSC_HANDLER_FUNC(led_level_map_handler) {
+ monome_t *monome = user_data;
+ uint8_t buf[64];
+ int i;
+
+ for( i = 0; i < 64; i++ )
+ buf[i] = argv[i + (argc - 64)]->i;
+
+ return monome_led_level_map(monome, argv[0]->i, argv[1]->i, buf);
+}
+
+OSC_HANDLER_FUNC(led_level_col_handler) {
+ monome_t *monome = user_data;
+ uint8_t buf[32];
+ int i;
+
+ if( argc < 3 || argc > 34 )
+ return 1;
+
+ for( i = 0; i < argc; i++ )
+ if( types[i] != 'i' ) /* only integers are invited to this party */
+ return 1;
+
+ for( i = 0; i < (argc - 2); i++ )
+ buf[i] = argv[i + 2]->i;
+
+ return monome_led_level_col(monome, argv[0]->i, argv[1]->i, argc - 2, buf);
+}
+
+OSC_HANDLER_FUNC(led_level_row_handler) {
+ monome_t *monome = user_data;
+ uint8_t buf[32];
+ int i;
+
+ if( argc < 3 || argc > 34 )
+ return 1;
+
+ for( i = 0; i < argc; i++ )
+ if( types[i] != 'i' )
+ return 1;
+
+ for( i = 0; i < (argc - 2); i++ )
+ buf[i] = argv[i + 2]->i;
+
+ return monome_led_level_row(monome, argv[0]->i, argv[1]->i, argc - 2, buf);
+}
+
+OSC_HANDLER_FUNC(led_ring_set_handler) {
+ monome_t *monome = user_data;
+
+ return monome_led_ring_set(monome, argv[0]->i, argv[1]->i, argv[2]->i);
+}
+
+OSC_HANDLER_FUNC(led_ring_all_handler) {
+ monome_t *monome = user_data;
+
+ return monome_led_ring_all(monome, argv[0]->i, argv[1]->i);
+}
+
+OSC_HANDLER_FUNC(led_ring_map_handler) {
+ monome_t *monome = user_data;
+ uint8_t buf[64];
+ int i;
+
+ for( i = 0; i < 64; i++ )
+ buf[i] = argv[i + (argc - 64)]->i;
+
+ return monome_led_ring_map(monome, argv[0]->i, buf);
+}
+
+OSC_HANDLER_FUNC(led_ring_range_handler) {
+ monome_t *monome = user_data;
+
+ return monome_led_ring_range(monome, argv[0]->i, argv[1]->i, argv[2]->i, argv[3]->i);
+}
+
+OSC_HANDLER_FUNC(tilt_set_handler) {
+ monome_t *monome = user_data;
+
+ if( argv[1]->i )
+ return monome_tilt_enable(monome, argv[0]->i);
+ else
+ return monome_tilt_disable(monome, argv[0]->i);
+}
+
+#define METHOD(path) for( cmd_buf = osc_path(path, prefix); cmd_buf; \
+ s_free(cmd_buf), cmd_buf = NULL )
+
+void osc_register_methods(sosc_state_t *state) {
+ char *prefix, *cmd_buf;
+ monome_t *monome;
+ lo_server srv;
+
+ prefix = state->config.app.osc_prefix;
+ monome = state->monome;
+ srv = state->server;
+
+#define REGISTER(typetags, cb) \
+ lo_server_add_method(srv, cmd_buf, typetags, cb, monome)
+
+ METHOD("grid/led/set")
+ REGISTER("iii", led_set_handler);
+
+ METHOD("grid/led/all")
+ REGISTER("i", led_all_handler);
+
+ METHOD("grid/led/map")
+ REGISTER("iiiiiiiiii", led_map_handler);
+
+ METHOD("grid/led/col")
+ REGISTER(NULL, led_col_handler);
+
+ METHOD("grid/led/row")
+ REGISTER(NULL, led_row_handler);
+
+ METHOD("grid/led/intensity")
+ REGISTER("i", led_intensity_handler);
+
+ METHOD("grid/led/level/set")
+ REGISTER("iii", led_level_set_handler);
+
+ METHOD("grid/led/level/all")
+ REGISTER("i", led_level_all_handler);
+
+ METHOD("grid/led/level/map")
+ REGISTER("ii"
+ "iiiiiiii"
+ "iiiiiiii"
+ "iiiiiiii"
+ "iiiiiiii"
+ "iiiiiiii"
+ "iiiiiiii"
+ "iiiiiiii"
+ "iiiiiiii",
+ led_level_map_handler);
+
+ METHOD("grid/led/level/col")
+ REGISTER(NULL, led_level_col_handler);
+
+ METHOD("grid/led/level/row")
+ REGISTER(NULL, led_level_row_handler);
+
+ METHOD("ring/set")
+ REGISTER("iii", led_ring_set_handler);
+
+ METHOD("ring/all")
+ REGISTER("ii", led_ring_all_handler);
+
+ METHOD("ring/map")
+ REGISTER("i"
+ "iiiiiiii"
+ "iiiiiiii"
+ "iiiiiiii"
+ "iiiiiiii"
+ "iiiiiiii"
+ "iiiiiiii"
+ "iiiiiiii"
+ "iiiiiiii",
+ led_ring_map_handler);
+
+ METHOD("ring/range")
+ REGISTER("iiii", led_ring_range_handler);
+
+ METHOD("tilt/set")
+ REGISTER("ii", tilt_set_handler);
+
+#undef REGISTER
+}
+
+void osc_unregister_methods(sosc_state_t *state) {
+ char *prefix, *cmd_buf;
+ lo_server srv;
+
+ prefix = state->config.app.osc_prefix;
+ srv = state->server;
+
+#define UNREGISTER(typetags) \
+ lo_server_del_method(srv, cmd_buf, typetags)
+
+ METHOD("grid/led/set")
+ UNREGISTER("iii");
+
+ METHOD("grid/led/all")
+ UNREGISTER("i");
+
+ METHOD("grid/led/map")
+ UNREGISTER("iiiiiiiiii");
+
+ METHOD("grid/led/col")
+ UNREGISTER(NULL);
+
+ METHOD("grid/led/row")
+ UNREGISTER(NULL);
+
+ METHOD("grid/led/intensity")
+ UNREGISTER("i");
+
+ METHOD("grid/led/level/set")
+ UNREGISTER("iii");
+
+ METHOD("grid/led/level/all")
+ UNREGISTER("i");
+
+ METHOD("grid/led/level/map")
+ UNREGISTER("ii"
+ "iiiiiiii"
+ "iiiiiiii"
+ "iiiiiiii"
+ "iiiiiiii"
+ "iiiiiiii"
+ "iiiiiiii"
+ "iiiiiiii"
+ "iiiiiiii");
+
+ METHOD("grid/led/level/col")
+ UNREGISTER(NULL);
+
+ METHOD("grid/led/level/row")
+ UNREGISTER(NULL);
+
+ METHOD("ring/set")
+ UNREGISTER("iii");
+
+ METHOD("ring/all")
+ UNREGISTER("ii");
+
+ METHOD("ring/map")
+ UNREGISTER("i"
+ "iiiiiiii"
+ "iiiiiiii"
+ "iiiiiiii"
+ "iiiiiiii"
+ "iiiiiiii"
+ "iiiiiiii"
+ "iiiiiiii"
+ "iiiiiiii");
+
+ METHOD("ring/range")
+ UNREGISTER("iiii");
+
+ METHOD("tilt/set")
+ UNREGISTER("ii");
+
+#undef UNREGISTER
+}
+
+#undef METHOD
299 src/osc/sys_methods.c
@@ -0,0 +1,299 @@
+/**
+ * Copyright (c) 2010 William Light <wrl@illest.net>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <lo/lo.h>
+#include <monome.h>
+
+#include "serialosc.h"
+#include "osc.h"
+
+
+/**
+ * utils
+ */
+
+static int portstr(char *dest, int src) {
+ return snprintf(dest, 6, "%d", src);
+}
+
+/**
+ * /sys/info business
+ */
+
+typedef void (info_reply_func_t)(lo_address *, sosc_state_t *);
+
+static int info_prop_handler(lo_arg **argv, int argc, void *user_data,
+ info_reply_func_t cb) {
+ sosc_state_t *state = user_data;
+ const char *host = NULL;
+ char port[6];
+ lo_address *dst;
+
+ if( argc == 2 )
+ host = &argv[0]->s;
+ else
+ host = lo_address_get_hostname(state->outgoing);
+
+ portstr(port, argv[argc - 1]->i);
+
+ if( !(dst = lo_address_new(host, port)) ) {
+ fprintf(stderr, "sys_info_handler(): error in lo_address_new()");
+ return 1;
+ }
+
+ cb(dst, state);
+ lo_address_free(dst);
+
+ return 0;
+}
+
+static int info_prop_handler_default(void *user_data, info_reply_func_t cb) {
+ sosc_state_t *state = user_data;
+ cb(state->outgoing, state);
+ return 0;
+}
+
+#define DECLARE_INFO_REPLY_FUNC(prop, typetag, ...)\
+ static void info_reply_##prop(lo_address *to, sosc_state_t *state) {\
+ lo_send_from(to, state->server, LO_TT_IMMEDIATE, "/sys/" #prop,\
+ typetag, __VA_ARGS__);\
+ }
+
+#define DECLARE_INFO_HANDLERS(prop)\
+ OSC_HANDLER_FUNC(sys_info_##prop##_handler) {\
+ return info_prop_handler(argv, argc, user_data, info_reply_##prop);\
+ }\
+ OSC_HANDLER_FUNC(sys_info_##prop##_handler_default) {\
+ return info_prop_handler_default(user_data, info_reply_##prop);\
+ }
+
+#define DECLARE_INFO_PROP(prop, typetag, ...)\
+ DECLARE_INFO_REPLY_FUNC(prop, typetag, __VA_ARGS__)\
+ DECLARE_INFO_HANDLERS(prop)
+
+DECLARE_INFO_PROP(id, "s", monome_get_serial(state->monome))
+DECLARE_INFO_PROP(size, "ii", monome_get_cols(state->monome),
+ monome_get_rows(state->monome))
+DECLARE_INFO_PROP(host, "s", lo_address_get_hostname(state->outgoing))
+DECLARE_INFO_PROP(port, "i", atoi(lo_address_get_port(state->outgoing)))
+DECLARE_INFO_PROP(prefix, "s", state->config.app.osc_prefix)
+
+static void info_reply_rotation(lo_address *to, sosc_state_t *state) {
+ if( monome_get_cols(state->monome) != monome_get_rows(state->monome) )
+ info_reply_size(to, state);
+
+ lo_send_from(to, state->server, LO_TT_IMMEDIATE, "/sys/rotation", "i",
+ monome_get_rotation(state->monome) * 90);
+}
+
+DECLARE_INFO_HANDLERS(rotation);
+
+static void info_reply_all(lo_address *to, sosc_state_t *state) {
+ info_reply_id(to, state);
+ info_reply_size(to, state);
+ info_reply_host(to, state);
+ info_reply_port(to, state);
+ info_reply_prefix(to, state);
+ info_reply_rotation(to, state);
+}
+
+OSC_HANDLER_FUNC(sys_info_handler) {
+ return info_prop_handler(argv, argc, user_data, info_reply_all);
+}
+
+OSC_HANDLER_FUNC(sys_info_handler_default) {
+ return info_prop_handler_default(user_data, info_reply_all);
+}
+
+/**/
+
+
+OSC_HANDLER_FUNC(sys_mode_handler) {
+ monome_t *monome = user_data;
+
+ return monome_mode(monome, argv[0]->i);
+}
+
+OSC_HANDLER_FUNC(sys_cable_legacy_handler) {
+ sosc_state_t *state = user_data;
+ monome_rotate_t new, old;
+
+ old = monome_get_rotation(state->monome);
+
+ switch( argv[0]->s ) {
+ case 'L':
+ case 'l':
+ case '0':
+ new = MONOME_ROTATE_0;
+ break;
+
+ case 'T':
+ case 't':
+ case '9':
+ new = MONOME_ROTATE_90;
+ break;
+
+ case 'R':
+ case 'r':
+ case '1':
+ new = MONOME_ROTATE_180;
+ break;
+
+ case 'B':
+ case 'b':
+ case '2':
+ new = MONOME_ROTATE_270;
+ break;
+
+ default:
+ return 1;
+ }
+
+ if( old == new )
+ return 0;
+
+ monome_set_rotation(state->monome, new);
+ info_reply_rotation(state->outgoing, state);
+ return 0;
+}
+
+OSC_HANDLER_FUNC(sys_rotation_handler) {
+ sosc_state_t *state = user_data;
+ monome_rotate_t new, old;
+
+ old = monome_get_rotation(state->monome);
+ new = argv[0]->i / 90;
+
+ if( old == new )
+ return 0;
+
+ monome_set_rotation(state->monome, new);
+ info_reply_rotation(state->outgoing, state);
+ return 0;
+}
+
+OSC_HANDLER_FUNC(sys_port_handler) {
+ sosc_state_t *state = user_data;
+ lo_address *new, *old = state->outgoing;
+ char port[6];
+
+ portstr(port, argv[0]->i);
+
+ if( !(new = lo_address_new(lo_address_get_hostname(old), port)) ) {
+ fprintf(stderr, "sys_port_handler(): error in lo_address_new()\n");
+ return 1;
+ }
+
+ state->outgoing = new;
+
+ info_reply_port(old, state);
+ info_reply_port(new, state);
+
+ lo_address_free(old);
+
+ return 0;
+}
+
+OSC_HANDLER_FUNC(sys_host_handler) {
+ sosc_state_t *state = user_data;
+ lo_address *new, *old = state->outgoing;
+
+ if( !(new = lo_address_new(&argv[0]->s, lo_address_get_port(old))) ) {
+ fprintf(stderr, "sys_host_handler(): error in lo_address_new()\n");
+ return 1;
+ }
+
+ state->outgoing = new;
+
+ info_reply_host(old, state);
+ info_reply_host(new, state);
+
+ lo_address_free(old);
+
+ return 0;
+}
+
+OSC_HANDLER_FUNC(sys_prefix_handler) {
+ sosc_state_t *state = user_data;
+ char *new, *old = state->config.app.osc_prefix;
+
+ if( argv[0]->s != '/' )
+ /* prepend a slash */
+ new = s_asprintf("/%s", &argv[0]->s);
+ else
+ new = s_strdup(&argv[0]->s);
+
+ osc_unregister_methods(state);
+ state->config.app.osc_prefix = new;
+ osc_register_methods(state);
+
+ info_reply_prefix(state->outgoing, state);
+
+ s_free(old);
+
+ return 0;
+}
+
+void osc_register_sys_methods(sosc_state_t *state) {
+ char *cmd;
+
+#define METHOD(path) for( cmd = "/sys/" path; cmd; cmd = NULL )
+#define REGISTER(types, handler, context) \
+ lo_server_add_method(state->server, cmd, types, handler, context)
+#define REGISTER_INFO_PROP(prop) do {\
+ METHOD("info/" #prop) {\
+ REGISTER("si", sys_info_##prop##_handler, state);\
+ REGISTER("i", sys_info_##prop##_handler, state);\
+ REGISTER("", sys_info_##prop##_handler_default, state);\
+ } } while ( 0 )
+
+ REGISTER_INFO_PROP(id);
+ REGISTER_INFO_PROP(size);
+ REGISTER_INFO_PROP(host);
+ REGISTER_INFO_PROP(port);
+ REGISTER_INFO_PROP(prefix);
+ REGISTER_INFO_PROP(rotation);
+
+ METHOD("info") {
+ REGISTER("si", sys_info_handler, state);
+ REGISTER("i", sys_info_handler, state);
+ REGISTER("", sys_info_handler_default, state);
+ }
+
+ METHOD("mode")
+ REGISTER("i", sys_mode_handler, state->monome);
+
+ METHOD("cable")
+ REGISTER("s", sys_cable_legacy_handler, state);
+
+ METHOD("rotation")
+ REGISTER("i", sys_rotation_handler, state);
+
+ METHOD("port")
+ REGISTER("i", sys_port_handler, state);
+
+ METHOD("host")
+ REGISTER("s", sys_host_handler, state);
+
+ METHOD("prefix")
+ REGISTER("s", sys_prefix_handler, state);
+
+#undef REGISTER
+#undef METHOD
+}
39 src/osc/util.c
@@ -0,0 +1,39 @@
+/**
+ * Copyright (c) 2010 William Light <wrl@illest.net>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifdef __STRICT_ANSI__
+#undef __STRICT_ANSI__
+#endif
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+
+#include "osc.h"
+
+char *osc_path(const char *path, const char *prefix) {
+ char *buf;
+
+ if( !(buf = s_asprintf("%s/%s", prefix, path)) ) {
+ fprintf(stderr, "aieee, could not allocate memory in "
+ "osc_path(), bailing out!\n");
+
+ /* in a child process, use _exit() instead of exit() */
+ _exit(EXIT_FAILURE);
+ }
+
+ return buf;
+}
44 src/platform/darwin.c
@@ -0,0 +1,44 @@
+/**
+ * Copyright (c) 2011 William Light <wrl@illest.net>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdlib.h>
+#include <sys/stat.h>
+
+#include "platform.h"
+